[转载(有删改)] 顺序表

申明:转自    http://www.cnblogs.com/Romi/archive/2012/01/07/2315788.html

一个线性表是n个数据元素的有限序列,线性结构的顺序表示指的是用一组地址连续的存储单元一次存储线性表的数据元素,以元素在计算机内"物理位置相邻"来表示线性表中数据元素之间的逻辑关系。

线性表的顺序存储结构是一种随机存取的存储结构,通常用数组来描述数据结构中的顺序存储结构。

以下将从(顺序表构造——顺序表初始化——插入元素——删除元素——元素比较——两个顺序表比较)等方面介绍。

1.顺序表构造

顺序表构造前进行如下宏定义和变量替换,方便代码的理解:

 1 #define TRUE 1
 2 #define FALSE 0
 3 #define OK 1
 4 #define ok 1
 5 #define ERROR 0
 6 #define error 0
 7 #define INFEASIBLE -1
 8
 9 #define LIST_INIT_SIZE 100
10 #define LISTINCREMENT 10
11
12 typedef int ElemType;
13 typedef int Status;

采用结构体构造一个顺序表,定义顺序表的地址、长度、存储容量的表示,代码如下:

1 typedef struct{
2     ElemType *elem;   //定义了顺序表中元素类型的数组指针,指向顺序表存储空间的基址
3     int length;       //顺序表的长度(也即元素个数)
4     int listsize;     //当前分配给顺序表的存储容量
5 }SqList;

2.顺序表的初始化

接下来对该顺序表进行初始化,为顺序表分配一个预定义大小的数组空间,并将当前顺序表长度设为0,如果元素个数大于分配的存储容量则再对容量进行扩充(初始化时不扩充,顺序表使用中按需要进行容量扩充)。代码如下:

 1 Status InitList(SqList *L)
 2 {
 3     (*L).elem=(ElemType*)malloc(100*sizeof(ElemType));
 4     //不知什么问题不能用LIST_INIT_SIZE,必须用100,下面的realloc函数也是一样?
 5     if((*L).elem==NULL)
 6     {
 7         exit(OVERFLOW);
 8     }
 9     (*L).length=0;
10     (*L).listsize=LIST_INIT_SIZE;
11     return ok;
12 }

malloc函数的使用:

函数原型:extern void *malloc(unsigned int num_bytes)

函数作用:向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针

头文件:VC中利用malloc函数时要加malloc.h或stdlib.h

返回值:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。

代码中(*L).elem=(ElemType*)malloc(100*sizeof(ElemType));这句话返回值类型被强制转换为ElemType*,因为返回值指向被配分内存指针(即顺序表*L的elem元素)。分配内存大小为100个ElemType所占字节

为了调试方便,创建一个顺序表并初始化,给其指定一定长度并向表中各元素赋值,代码如下:

1 SqList* L1=new SqList();
2 InitList(L1);  //测试InitList函数
3 (*L1).length=10;
4 for(int j=0;j<(*L1).length;j++)
5 {
6     (*L1).elem[j]=j;
7 }

注意:定义了指针变量后,一定要将其初始化。一个不指向任何地址空间的指针是十分危险的!上面定义了一个SqList结构体指针变量并对其分配了内存空间。

为方便测试、简化代码量,写一个输出函数以供调用,用于输出顺序表L的个元素。代码如下:

1 void Output_L(SqList *L)
2 {
3     for(int i=0;i<(*L).length;i++)
4         cout<<(*L).elem[i]<<" ";
5 }

可以调用Output_L函数看下顺序表L1的输出情况:Output_L(L1);   显示结果如下:

初始化的这个顺序表L1在本文后面进行测试时会大量用到。

3.顺序表中元素的插入

功能:在顺序表第i个位置前加入新的元素e。

思路:第i个位置加入元素e,那么原来在第i个位置的元素就要移到i+1个位置中,依次向后推。移完后顺序表长度+1。关键是如何移动的问题,如果从先插人e再移动,则插入后第i个位置的值是e,原先第i个位置的值就不知道了,因为存储空间被e占用了,因此移动前还需要用个地址保存原先位置上的值。这样比较麻烦,我们采用高低址向低地址移动,先将顺序表最后一个位置(假设为p)的值传入(P+1)中,这样位置p就空出来了,将p-1的值放入p中,以此类推,最后第i个位置也空出来了,将e放入其中,这样就完成了顺序表的插入工作。

因为要插入元素,所以首先判断插入的位置是否正确,再判断存储空间够不够,不够就增加内存分配。

注意:这里我采用的是插入指针e中的值到顺序表的第i个位置,因为ListInsert这个函数还要用到后面的一些功能中,我在学习过程中调试过很多次,有的函数参数用指针,有的函数参数没用指针,在进行函数调用时很容易出错,所以呢这里统一使用指针,以免函数调用时参数出现各种不匹配,改又不好改。代码如下:

 1 Status ListInsert(SqList (*L),int i,ElemType *e)
 2 {
 3     ElemType *newbase,*p,*q;
 4     if(i<1||i>(*L).length+1)
 5     {
 6         return ERROR;
 7     }
 8     if((*L).length>=(*L).listsize)
 9     {
10         newbase=(ElemType *)realloc((*L).elem,((*L).listsize+10)*sizeof(ElemType));
11         //不能用LISTINCREMENT,必须用10,下面一行就能用,为甚么?和realloc这个函数有关系吗
12         (*L).elem=newbase;
13        (*L).listsize=(*L).listsize+LISTINCREMENT;
14     }
15     q=&((*L).elem[i-1]);
16     for(p=&((*L).elem[(*L).length-1]);p>=q;p--)
17     {
18         *(p+1)=*p;
19     }
20     *q=*e;
21     (*L).length++;
22     return ok;
23 }

需要注意的是for循环中的循环终止条件。

realloc函数的使用:

函数原型:extern void *realloc(void *mem_address, unsigned int newsize)

参数意义:void*表示返回指针的数据类型,可以强制转换为其他类型(这点和malloc是一样的);函数第一个参数表示要改变内存大小的指针名,即要改变的是哪个指针指向的内存;第二个参数表示要分配的新的大小,新的大小一定要大于原来的大小不然会导致数据丢失。

头文件:#include <stdlib.h> 有些编译器需要#include <alloc.h>

功能:先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

返回值:如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

注意:这里原始内存中的数据还是保持不变的。当内存不再使用时,应使用free()函数将内存块释放。

下面对ListInsert这个函数进行测试,假设将指针LI2中的值插入顺序表L1中第5个位置,代码如下:

1 ElemType LI1=8;
2 ElemType *LI2;
3 LI2=&LI1;
4 ListInsert(L1,5,LI2);
5 cout<<"测试ListInsert函数:"<<endl;
6 Output_L(L1);
7 cout<<endl;

调试结果显示如下:

4.顺序表中元素的删除

功能:将顺序表的第i个位置的元素参数,并将该元素存到指针e中

思路:删除第i个位置的元素后,后面所有的元素所在的位置都要向前移一位。与元素添加后移位的方法相反,元素删除后我们采取从地址低的位置向地址高的位置移动。因为是删除元素,所以就不必判断存储空间够不够了,但删除的位置是否靠谱还是必须要判断的。不要忘了最后顺序表的长度要-1。

代码如下:

 1 Status ListDelete(SqList *L,int i,ElemType *e)
 2 {
 3     ElemType *p,*q;
 4     if(i<0||i>=(*L).length)
 5     {
 6         return error;
 7     }
 8     q=&((*L).elem[i-1]);   //q为被删除元素的位置
 9     *e=*q;
10     p=&((*L).elem[(*L).length-1]);  //p指向顺序表最后一个元素位置的地址
11     for(q;q<p;q++)
12     {
13         *q=*(q+1);
14     }
15     (*L).length--;
16     return ok;
17 }

需要注意的是for循环中的循环终止条件,可以和元素插入算法相比较下,有什么差别。

对ListDelete函数进行测试,假设删除的是顺序表中L1第4个位置的元素,并将元素存储到指针变量e中,代码如下:

1 ElemType *e=new ElemType();
2 cout<<"测试ListDelete函数:"<<endl;
3 ListDelete(L1,4,e);
4 Output_L(L1);
5 cout<<endl;
6 cout<<"e="<<*e<<endl;

调试结果显示如下:

5.顺序表中元素比较

功能:在顺序线性表中查找第一个与指针e中的值满足Compare()关系的元素的位置,返回该位置,没有就返回0。

首先,先定义一下Compare()这个函数,就假设这个关系是相等关系吧,利用Compare这个函数实现,如果两个指针中的值相等就返回true,不相等就返回false。代码如下:

1 bool Compare(ElemType* e1,ElemType* e2)    //参数要就都用指针表示,以免函数间相互调用时参数出现不匹配问题
2 {
3     if(*e1==*e2)
4         return true;
5     else
6         return false;
7 }

根据这个关系,要找出顺序表中的元素,明显的思路就是一个个元素进行对比了,看是否满足Compare()关系。这里需要注意的一点是元素的位置和数组元素的表示,第i个位置的元素是(*L).elem[i-1]。代码如下:

 1 int LocateElem(SqList *L,ElemType *e)
 2 {
 3     int i=1;       //i为顺序表中的位置
 4     ElemType *p;   //p指向顺序表中位序为i的元素
 5     p=(*L).elem;     //取数组首元素地址
 6     while(i<=(*L).length&&(!Compare(p,e)))
 7     {
 8         i++;
 9         p++;
10     }
11     if(i<=(*L).length)
12         return i;       //返回满足条件的元素所在位置i
13     else
14         return 0;
15 }

对LocateElem函数进行测试,代码如下

1 ElemType LE1=4;
2 ElemType *LE2;
3 LE2=&LE1;
4 cout<<"测试LocateElem函数:"<<endl;
5 int a=LocateElem(L1,LE2);
6 cout<<LE1<<"元素的位置:"<<a<<endl;

调试显示结果如下:

6.两个顺序表之间的运算

功能:将存在线性表Lb中而不存在La中的数据元素插入到线性表La中,是在La中元素后面依次插入,没有按什么顺序插入。

思路:这算是顺序表应用的小综合,首先要判断线性表Lb中的各元素在线性表La中是否存在,要用到LocateElem函数,还要对元素进行插入,要用到ListInsert函数。因为这两个函数前面都理解的很透彻了,再来完成这个算法想必会比较简单。

算法涉及到取元素,定义一个函数GetElem()将线性表L中第i个元素存入指针e中,代码如下:

1 void GetElem(SqList *L,int i,ElemType *e)
2 {
3     if(i>0&&i<=(*L).length)
4         *e=(*L).elem[i-1];
5 }

利用这个函数和前面的两个函数完成该算法就很简单了,直接放代码,如下:

 1 void Collect(SqList *La,SqList *Lb)
 2 {
 3     ElemType *e=new ElemType();    //局部指针变量分配了内存空间,用完后要注意释放内存空间
 4     int La_len=(*La).length;
 5     int Lb_len=(*Lb).length;
 6     for(int i=1;i<=Lb_len;i++)
 7     {
 8         GetElem(Lb,i,e);
 9         if(!LocateElem(La,e))
10         {
11             ListInsert(La,La_len+1,e);   //注意这个时候La的长度并没有加1
12             La_len++;
13         }
14     }
15     delete(e);
16 }

对该算法进行测试,测试时要初始化另外一个顺序表L2,代码如下:

 1 cout<<"测试Connect函数:"<<endl;
 2 SqList *L2=new SqList();
 3 InitList(L2);   //定义了SqList变量并初始化后,在赋值之前千万别忘了使用该函数构造空一个线性表
 4 (*L2).length=10;
 5 for(int j=0;j<(*L2).length;j++)
 6 {
 7     (*L2).elem[j]=j*2;
 8 }
 9 cout<<"线性表L2:"<<endl;
10 Output_L(L2);
11 cout<<endl;
12 Collect(L1,L2);
13 cout<<"执行Connect函数后的线性表L1:"<<endl;
14 Output_L(L1);

调试显示结果如下:

注意:程序最后不要忘了将用new分配了内存空间的指针变量用delete释放内存空间。顺序表如果不用了,申请的内存空间要free掉。

时间: 2024-10-14 16:59:29

[转载(有删改)] 顺序表的相关文章

【转载】顺序表中删除为x的所有元素,时间空间复杂度有要求

解法一: 首先确定顺序表L中的第一个值为x的元素位置i,然后依次检查L.data[i+1]~L.data[L.length-1]中每个元素L.data[j](i+1<=j<L.length),若L.data[j]!=x,则将L.data[j]存入L.data[i]中,并令i增1.最后顺序表长度为i.算法如下: void delall(Sqlist *l,int x) { int i=0,j; while(i<l->length && l->data[i]!=x

顺序表的插入和删除、数组的增删改查和二分查找

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px "PingFang SC" } span.s1 { font: 24.0px Helvetica } 1.顺序表的表示 为实现顺序表的操作,首先要将其表示出来,用同数据类型的数组和表示数组的长度的整型变量表示. public class LineList{ private int[] data; private int length; public LineList(){ } pu

2、顺序表

|   版权声明:本文为博主原创文章,未经博主允许不得转载. 从这节开始,以后的每一篇博文均会只描述一种结构.这节要解决的是有关顺序表的问题,下面就是一些顺序表的基本的知识点: 1. 顺序表其实就是线性表的一种,它除开始节点和结束节点之外的其他节点,均有且只有一个直接前趋和一个直接后继,开始 节点只有一个后继节点,结束节点只有一个前趋节点. 2. 用顺序存储方法存储的线性表就称为顺序表. 3. 顺序存储方法就是将表的节点按逻辑上的次序依次的存放在一组连续的内存单元中,这里是指在逻辑上连续排列,在

顺序表的非递减数列合并

#include<stdio.h> /*包含输入输出头文件*/ #define ListSize 100 typedef int DataType; typedef struct { DataType list[ListSize]; int length; }SeqList; void InitList(SeqList *L) /*将线性表初始化为空的线性表只需要把线性表的长度length置为0*/ { L->length=0; /*把线性表的长度置为0*/ } int ListEmpt

顺序表的实现

  顺序表增删改查功能的实现 ————————数据结构C++版 #include<iostream> using namespace std; # define DefaultListsize  10 template <class Elem> class List                                       //类模板,其中Elem为抽象数据类型 { virtual void clear()=0; virtual bool insert(Elem&

稀疏矩阵的三元组顺序表存储及矩阵相乘算法小结

稀疏矩阵的三元组顺序表存储及矩阵相乘算法小结 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 一:稀疏矩阵的三元组顺序表数据结构 typedef int ElemType; typedef struct { intx, y;  //该非零元素的行下标和列下标 ElemTypee; //该非零元素的值 } Triple; typedef struct { Tripledata[MAXSIZE]; //非零元素三元组顺序表 intmu, nu, t

顺序表中的思路

在c语言当中判断数组的长度是比较麻烦的,不像其他语言可以通过对象的属性或者方法来获得长度. int arr[4]; int length = sizeof(arr)/sizeof(arr[0]); //数组占内存总空间,除以单个元素占内存空间大小 而有一种类似数组的数据结构叫线性表,可以用来很方便的获取数组的长度.其结构体为 typedef struct { int data[1024]; int last; } List; 该数据结构除了定义了一个长度为1024的数组外,还多定义一个变量las

Java数据结构-线性表之顺序表ArrayList

线性表的顺序存储结构,也称为顺序表,指用一段连续的存储单元依次存储线性表中的数据元素. 根据顺序表的特性,我们用数组来实现顺序表,下面是我通过数组实现的Java版本的顺序表. package com.phn.datestructure; /** * @author 潘海南 * @Email [email protected] * @TODO 顺序表 * @date 2015年7月16日 */ public class FOArrayList<E> { // 顺序表长度 private int

数据结构顺序表的操作全集(创建,遍历,插入,删除,排序等等)

#include"stdio.h" #include"stdlib.h" #include"malloc.h" #define list_size 100 typedef struct Node { int data[list_size]; int len; }NODE,* PNODE; void creat_list(PNODE L) { int i; int val; int len; /* PNODE L=(PNODE)malloc(siz