javascript实现数据结构:线性表--简单示例及线性表的顺序表示和实现
线性表(linear list)是最常用且最简单的一种数据结构。一个线性表是n个数据元素的有限序列。在稍复杂的线性表中,一个数据元素可以由若干个数据项(item)组成。
其中:
- 数据元素的个数n定义为表的长度 = "list".length() ("list".length() = 0(表里没有一个元素)时称为空表)
- 将非空的线性表(n>=0)记作:(a[0],a[1],a[2],…,a[n-1])
- 数据元素a[i](0≤i≤n-1)只是个抽象符号,其具体含义在不同情况下可以不同
一个数据元素可以由若干个数据项组成。数据元素称为记录,含有大量记录的线性表又称为文件。这种结构具有下列特点:存在一个唯一的没有前驱的(头)数据元素;存在一个唯一的没有后继的(尾)数据元素;此外,每一个数据元素均有一个直接前驱和一个直接后继数据元素。
线性表的存储结构
在这种情况下,常把数据元素称为记录(record),含有大量记录的线性表又称文件(file)。
1.假设利用两个线性表LA和LB分别表示两个集合A和B,现要求一个新的集合A=AUB。这就要求对线性表作如下操作:扩大线性表LA,将存于线性表LB中而不存在于
线性表LA中的数据元素插入到线性表LA中去。只要从线性表LB中依次取得每个数据元素,并依值在线性表LA中进行查找,若不存在,则插入之。
下面是算法描述(这里用js的数组来表示线性表):
1 // 将所有在数组b中但不在数组a的数据元素插入到a中 2 3 var a = [1, 2, 3, 4, 5]; 4 var b = [1, 3, 5, 7, 9]; 5 6 function union(a, b) { 7 var elem, equal; 8 9 for (var i = 0, bLen = b.length; i < bLen; i++) { 10 elem = b[i]; 11 equal = false; 12 13 for (var j = 0, aLen = a.length; j < aLen; j++) { 14 if (elem === a[j]) { 15 equal = true; 16 break; 17 } 18 } 19 20 if (!equal) a.push(elem); 21 } 22 } 23 24 union(a, b); 25 console.log(a); 26 // [1, 2, 3, 4, 5, 7, 9] 27 28 // 时间复杂度:O(aLen * bLen)
2.已知线性表LA和LB中的数据元素按值非递减有序排列,现要求将LA和LB归并为一个新的线性表LC,且LC中的数据元素仍按值非递减有序排列:
我们可设两个变量分别保存LA和LB的索引,并对对应的元素进行比较:
1 1 // 已知数组a和数组b中的数据元素按值非递减排列 2 2 // 归并a和b得到新的数组c,c的数据元素也按值非递减排列 3 3 var a = [3, 5, 8, 11]; 4 4 var b = [2, 6, 8, 9, 11, 15, 20]; 5 5 6 6 function mergeList(a, b) { 7 7 var c = [], aElem, bElem; 8 8 var i = 0, j = 0, k = 0; 9 9 var aLen = a.length; 10 10 var bLen = b.length; 11 11 12 12 while (i < aLen && j < bLen) { 13 13 aElem = a[i]; 14 14 bElem = b[j]; 15 15 16 16 if (aElem < bElem) { 17 17 c[k++] = aElem; 18 18 i++; 19 19 } else { 20 20 c[k++] = bElem; 21 21 j++; 22 22 } 23 23 } 24 24 25 25 while (i < aLen) { 26 26 c[k++] = a[i++]; 27 27 } 28 28 29 29 while (j < bLen) { 30 30 c[k++] = b[j++]; 31 31 } 32 32 33 33 return c; 34 34 } 35 35 36 36 var c = mergeList(a, b); 37 37 console.log(c); 38 38 // [2, 3, 5, 6, 8, 8, 9, 11, 11, 15, 20] 39 39 40 40 // 时间复杂度: O(aLen + bLen)
线性表的顺序表示和实现
线性表的顺序表示指的是用一组地址连续的存储单元一次存储线性表的数据元素。
假设线性表的每个元素需暂用l个存储单元,并以所占的第一个单元的存储地址为左数据元素的存储位置。则线性表中第i1个数据元素的存储位置LOC(a(i+1))和第i个元素的存储位置LOC(a(i))之间满足下列关系:
LOC(a(i + 1)) = LOC(a(i)) + l;
线性表的这种表示称做线性表的顺序存储结构或顺序映射(sequential mapping)。通常,,称这种存储结构的线性表为顺序表。它的特点是,为表中相邻的元素a(i)和a(i+1)赋以相邻的存储位置LOC(a(i))
和LOC(a(i+1))。换句话说,以元素在计算机内“物理位置相邻”来表示线性表中数据元素之间的逻辑关系。每一个数据元素的存储位置都和线性表的起始位置相差1一个和数据元素在线性表中的位序成正比的常数。由此,只要确定了存储线性表的起始位置,线性表中任意数据元素都可随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构。
由于高级程序设计语言中的数组类型也有随机存取的特性,因此,通常都用数组来描述数据结构中的顺序存储结构。
为了更清楚线性表的顺序表示,我们实现js的伪数组进行模拟,插入和删除元素:
1 // 使用伪数组模拟线性表插入操作的前后数据元素在存储空间中的位置变化 2 var a = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5}; 3 a.length = 6; 4 5 function insert(a, i, elem) { 6 if (!elem) return; 7 8 var len = a.length; 9 if (i >= len) { 10 while (len < i) { 11 a[len++] = undefined; 12 a.length++; 13 } 14 a[i] = elem; 15 } else { 16 while (len > i) { 17 a[len--] = a[len]; 18 } 19 a[i] = elem; 20 } 21 a.length++; 22 } 23 24 insert(a, 3, 8); 25 insert(a, 10, 10); 26 console.log(a); 27 28 // 使用伪数组模拟线性表删除操作的前后数据元素在存储空间中的位置变化 29 30 function del(a, i) { 31 var temp = a[i]; 32 var j = i + 1; 33 var len = a.length; 34 35 while (j < len) { 36 a[j - 1] = a[j++]; 37 } 38 a.length--; 39 delete a[len - 1]; 40 41 return temp; 42 } 43 44 del(a, 3); 45 console.log(a); 46 del(a, 10); 47 console.log(a); 48 49 // 时间复杂度: O(a.length)
线性表的顺序存储结构的特点是逻辑关系上相邻的两个元素在物理位置上也相邻,因此可以随机存取表中任一元素,它的存储位置可用一个简单,直观的公式来表示。然后,另一方面来看,这个特点也造成这种存储结构的弱点,在做插入或删除操作时,需移动大量元素。下一节会讨论这个问题的解决方法--链式存储结构。