线性表在采用不一样的存储结构时,它的描述方法是不同的,那么它的基本操做实现方法也大相径庭。下面来看线性表在顺序存储下,也就是顺序表的每个基本操做的具体实现方法以及编写方法。数组
仍是上一个例子,一群朋友去吃火锅。此时有一个女生过来了,她叫小红。小红是小绿的女友,固然想和小绿坐在一块儿,可是小绿旁边是没有空位置的。因此小绿麻烦他旁边的朋友小黄和小黑依次移动了一个位置。这样就空出了一个位置,小红就能够过来坐下了。这样就完成了一个相似顺序表插入的过程。数据结构
知道了顺序表插入的大体过程,来看它用程序语言是如何编写的。首先画出了一个顺序表函数
其中数据元素是连续存放的。接着标出了存放该顺序表数据元素的数组下标。注意一下,数组下标是从 0 开始的,而顺序表的元素标号是从 1 开始的。接着用 C++ 写出了该插入操做的函数体:学习
bool ListInsert(SqList &L, int i, ElemType e) { if(i<1 || i>L.length+1) return false; if(L.length >= MaxSize) return false; for(int j=L.length; j>=i; j--) L.data[j] = L.data[j-1]; L.data[i-1] = e; L.length++; return true; }
它的返回值类型是一个布尔类型,表示若是插入成功会返回一个 True ,插入失败会返回一个 False。它有三个参数,分别是一个引用类型的顺序表 L,这里为何用引用类型?由于在函数体内部进行操做,实际上是做用一个局部变量上的,不会真正做用到这个参数上,若是不用引用类型,是不能把插入操做真正做用到传入的顺序表上的。接着是一个整型变量 i,它表示的是插入的位置,要注意在采用插入操做时,每每是前插法,这是一个默认规定。还有一点要注意的就是,i 对应的是顺序表的表号而非数组的下标。最后一个参数是所插入的数据元素 e。3d
首先第一个条件 i<1 || i>L.length+1
,顺序表的元素标号是从 1 开始的,i 表示的是插入的位置,若是插入的位置是 0 或者更小,又或者比 L.length+1
还大,都是不合法的。第二个条件判断的是插入的数组是否有足够空间去插入,由于数组不能够增长容量,一旦满了就没用新的空间去提供插入了。循环语句中申请了变量 j 初始化为顺序表的长度,j 进行减一操做,一直到 j=i
的时候停止循环。循环体中 L.data[j] = L.data[j-1]
的意思就是把每个数据元素向后移了一位,一直移动到 ai 移动到原先 ai+1 的位置。 执行完了全部的循环,空出了一个位置用于插入,L.data[i-1] = e
就是把要插入的元素放到该位置。注意,在 i 的位置,元素对应的下标是 i-1。最后将顺序表的长度加一。code
再来看一下这个程序的时间复杂度,当循环次数最少时,它就是最好的时间复杂度,何时循环次数最少呢?发现当在顺序表的尾部,也就是 L.length+1
的时候,这时候不用进行元素的移动,它的循环次数是 0,因此它的最好时间复杂度是 O(1) ,由于它的语句频度是常数级别的。blog
接着来看平均时间复杂度,平均时间复杂度是在全部状况等几率的状况下,计算它的指望。这里的等几率,必定是合法的状况。因此 i 一共有 n+1 个合法位置,n 个数据元素,加上第 n 个数据元素以后的那个位置,均可以进行插入。因此每个插入等几率的状况下,它的几率是:class
接着根据几率的知识,能够用每个位置的几率乘以它循环的次数,它是从尾部一直循环到 i ,因此循环了 n-i+1
次,根据等差数列求和公式能够计算:变量
计算结果与 n 同阶无穷大,因此它的平均时间复杂度为 O(n) 。List
最坏就是循环次数最多,即每个数据元素都要向后移动一位,也就是插入在第一个位置时是最坏的状况,因此要循环 n 次,因此最坏状况下的时间复杂度是 O(n) 。
仍是一群小伙伴在等候区等待吃火锅,这时小绿被他的女友叫回去一块儿学习数据结构,那么小绿要走了,小绿走了以后就空出了一个位置。在等候区中间是不能够有空的位置的,因此说小黄和小黑两我的会依次向前移一位。
这也就是删除操做的大体过程。接着来看它的代码实现。
bool ListDelete(SqList &L, int i, ElemType &e) { if(i<1 || i>L.length) return false; e = L.data[i-1] for(int j=i; j<L.length; j++) L.data[j-1] = L.data[j]; L.length--; return true; }
函数的返回值依旧是一个布尔类型。它有三个参数,分别是一个引用类型的顺序表 L,一个整型变量 i 表示删除的位置,一个引用类型的数据元素 e ,这里使用引用类型也是由于在函数体内部进行操做,是做用在一个局部变量上的,不会真正做用到这个参数 e 上,因此说使用引用类型来将所删除的这个数据元素真正的返回到函数外部。
第一个条件是判断删除的位置是否合法。接着 e = L.data[i-1]
是在保存要删除的数据元素,将 i
位置的数据元素,对应下标 i-1
,赋值给 e
。而后循环从 i
开始,每次进行加一操做,一直循环到 L.length-1
为止,也就是从 i
一直循环到 n-1
,将 ai 赋值给 ai+1 ,也就是将数组下标为 i
的赋值给 i-1
的,这样就将 ai 以后的全部数据元素都向前移了一位。接着将顺序表的长度减一。
最好的时间复杂度是删除最后一个元素,它的最好时间复杂度是 O(1) 。
全部合法的删除位置有 n 个,由于有 n 个数据元素。因此每个合法位置的几率都是 1/n
,它的循环次数是从 i 循环到 L.length-1
,因此是 n-i
次,因此它的指望是 (n-1)/2
,它与 n 同阶无穷大,因此它的平均时间复杂度为 O(n) 。
删除第一个数据元素循环的次数最多,此时须要循环 n-1 次,与 n 同阶无穷大,所以最坏时间复杂度为 O(n) 。
须要找到等候区的小绿,等候区是这样依次坐下的。因而依次进行查找,从第一个位置开始,一直找到第三个位置,发现他是小绿。
这样的查找过程,就与按值查找的函数十分类似。
int Locate(SqList &L, ElemType e) { int i; for(i=0; i<L.length; i++) if(L.data[i] == e) return i+1; return 0; }
这个函数的返回值是一个整型变量,它表示返回的是一个顺序表的下标。两个参数分别是查找的顺序表以及查找的值。全部对输入参数进行修改的操做,都使用引用类型。全部进行查找的操做都不会使用引用类型。
首先是一个整型变量 i ,它用来存放最终的输出结果。接着是一个循环,变量 i 从下标 0,也就是第一个元素开始,每一次循环进行加一操做,一直循环到 L.length-1 ,也就是下标 n-1 的位置,一共执行了 n 次。循环体的内容是一个判断语句,判断此时数据元素是否为要找的那个元素,若是相等返回当前顺序表的下标,也就是 i+1。最后 return 0 表示循环结束了,尚未找到对应的这个值的位置,那么就说明顺序表当中是没有该值的,此时就返回一个 return 0 表示查找失败。
最好的时间复杂度依旧是循环的次数最少,也就是第一个数据元素就是要找的那个值,因此最好的时间复杂度为 O(1) 。
一共有 n 个元素,查找几率是 1/n
,查找次数是 n-i
次,它的指望是 (n+1)/2
,它与 n 同阶无穷大,因此它的平均时间复杂度为 O(n) 。
最后一个元素被找到是最坏的状况,循环了 n 次,所以最坏时间复杂度为 O(n) 。