顺序表及其基本操作实现(C语言实现)
顺序表是最简单的数据结构之一,在计算机中顺序表一般以数组的形式保存,我们都知道数组是线性保存的,因此顺序表也是线性保存的,线性表的连续存储值得是在计算机中用一块连续的存储空间保存线性表的元素,所以线性表的相邻元素是保存在连续的存储单元上的。
地址 | Loc(a) | Loc(a2) | Loc(a3) | … | Loc(an) | … |
---|---|---|---|---|---|---|
存储空间 | a1 | a2 | a3 | … | an | … |
下标 | 1 | 2 | 3 | … | n | … |
假设上图是顺序表a在计算机的存储情况,loc(an)表示的是下标为n的元素所在的存储单元的首地址,那么很容易看出,只要知道了顺序表的首地址loc(a1)就能根据公式loc(an)=loc(a1)+(n-1)k计算出任意一个元素的地址。
由于线性存储是顺序表的特性,所以我们在对它进行任何操作的时候都不能违背这个特性。比如要把a3这个元素删除,为了维持顺序表的结构,我们必须把a3后面的所有元素都依次往前搬移一个位置,同理,假设我们要把在a3和a4之间插入一个元素,就不得不把a4及a4以后的所有元素先往后搬移一个位置,从而给新元素预留一个位置出来。大量的搬移元素导致了顺序表的删除插入效率很低,其最坏时间复杂度为O(n);但是由于其线存储的特点,访问元素的效率很高,其时间复杂度仅为O(1);
对顺序表的各项操作都比较简单,下面给出线性表的操作实现(C语言实现)
1.顺序表结构体,由长度和数据数组组成:
1typedef struct
2{
3 int data[MAXNUM]; //数据数组
4 int length; //顺序表长度
5}list;
2.创建顺序表:
1void createlist(list *lp)
2{
3 int i, elem; //定义中间变量i和元素变量elem
4 lp->length = 0; //初始化长度为0
5 printf("\nplease input datas of the list\n");
6 for (i = 0; i< MAXNUM; i++)
7 {
8 scanf_s(" %d", &elem); //
9 if (elem == -1) break; //-1结束输入
10 lp->data[i] = elem; //输入元素保存到顺序表数组
11 lp->length++; //每保存一个元素长度加1
12 }
13}
3.遍历顺序表:
1void showlist(list *lp)
2{
3 int i;
4 printf("\nThese %d datas are:\n", lp->length);
5 if (lp->length <= 0) //长度<=0就没有元素,直接返回
6 {
7 printf("No data!\n");
8 return;
9 }
10 for (i = 0; i<lp->length; i++) // 遍历顺序表内部数组
11 printf(" %d ", lp->data[i]);
12 printf("\nlength of the list is:%d", lp->length);
13}
4.插入元素到指定位置:
1int insertlist(list *lp, int new_elem, int i) //new_elem:新元素;i:插入位置
2{
3 int j;
4 if (lp->length >= MAXNUM) // 达到最大元素数量将不能插入MAXNUM可以自定义
5 {
6 printf("the list is full,can not insert.");
7 return(false);
8 }
9 if (i<1 || i>lp->length + 1) // 位置大于顺序表的范围,不能插入
10 {
11 printf("\n%d is invalid value", i);
12 return(false);
13 }
14 for (j = lp->length; j >= i; j--) // 将插入位置之后的元素依次往后移动一个位置
15 lp->data[j] = lp->data[j - 1]; //从最后一个元素开始移动
16
17 lp->data[i - 1] = new_elem; //放入新元素;
18 lp->length++; //表长度增加;
19 return(true);
20}
5.删除某个位置的元素:
1 int delete(list *lp, int i) // lp:顺序表,i:删除元素的位置
2{
3 int j;
4 if (i<1 || i>lp->length) //删除的位置非法,即超出范围
5 {
6 printf("elem not exist");
7 return(false);
8 }
9//从删除元素之后的第一个元素开始依次将后面元素向前移动一个位置
10 for (j=i;j<=lp->length-1;j++)
11 lp ->data[j-1]=lp->data[j];
12 lp->length--; //表长度减1;
13 return(true);
14}
6.将所有元素倒序:
由于倒序代码虽然简短,但是在不借助另一个线性表的情况下实现思路比较巧妙,这里简单说明一下:
如上线性表,要倒序,就相当于a1和a6交换位置,a2和a5交换位置,a3和a4交换位置,得到的结果就是原来线性表的倒序了。
1void reverse_list(list *lp)
2{
3 int temp,i; // temp 中间变量,用于交换元素
4 for (i = 0; i < (lp->length / 2); i++) //把线性表分为前后两部分然后从两端开始依次进行交换
5 {
6 temp = lp->data[i];
7 lp->data[i] = lp->data[lp->length - i - 1];
8 lp->data[lp->length - i - 1] = temp;
9 }
10}
7.删除所有负数:
(1)删除所有负数最容易想到的办法就是循环遍历整个顺序表,然后判断元素的正负,调用删除函数删除,代码如下:
1void delete_negative(list *lp)
2{
3 int i,j;
4 for (i = 0; i <lp->length;) // 遍历顺序表
5 {
6 if (lp->data[i] < 0) // 判断正负
7 {
8 delete(lp,i) // 删除元素
9 }else i++; // i++千万不要写在for里面,否则出现连续两个负数的时候后一个将不被检查,导致漏删
10 }
11}
(2)通过调用删除函数的方式代码思路很简单,但是每删除一个元素就要把其后面的元素搬移一次,效率非常低,所以我们要考虑一种方式,看能不能不要每次删除都移动大量的元素。一种想法就是遍历数组,并且设置一个记录发现的第一个负数的位置的变量,然后继续遍历,当发现下一个正数的时候,用这个正数和之前发现的负数交换位置,用这种方式把负数逐渐往后推。具体流程图如下:
1.初始:定义两个变量i=0和j=0;i记录当前遍历到的位置,j记录已遍历到的负数的位置:i=0,j=0
1 | -3 | -4 | 5 | 2 |
---|---|---|---|---|
i=0,j=0 |
第一次循环后:
1 | -3 | -4 | 5 | 2 |
---|---|---|---|---|
i=1,j=1 |
第二次循环后:
1 | -3 | -4 | 5 | 2 |
---|---|---|---|---|
j=1 | i=2 |
第三次循环后:
1 | 5 | -4 | -3 | 2 |
---|---|---|---|---|
j=2 | i=3 |
第四次循环后:
1 | 5 | 2 | -3 | -4 |
---|---|---|---|---|
j=3 | i=4 |
结束循环:改变顺序表长度为j,把所有负数排除在顺序表外
1//优化后删除所有负数的函数
2void better_delete_negetive(list *lp)
3{
4 int i, j = 0; //初始化i,j
5 for (i = 0; i < lp->length; i++) // 循环遍历数组
6 {
7 if (lp->data[i] >= 0) // 如果是非负数
8 {
9 // 并且j<i说明前面j位置处有负数(事实上j与i之间的元素都是负数,并且j位置处是第一个负数的下标)
10 if (j<i){
11 lp->data[j] = lp->data[i];
12}
13 j++; 交换之后j++(事实上结合上面流程图,i != j 的时候,j 的位置就是负数,所以可以理解为定位到下一个负数)
14 }
15 }
16 lp->length = j; // 改变顺序表的长度,把负数排除在外(因为循环结束后aj以及后面的元素都是负数)
17}
以上便是关于顺序表的有关介绍和代码实现,由于水平有限,写得不当的地方欢迎大家指正!
微信公众号