本篇介绍数据结构-线性表
思维导图
线性表
-
定义
是由n(n≥0)个数据元素(结点)a1,a2....an组成的有限序列。其中,数据元素的个数n为表的长度。当n为零时称为空表。
-
逻辑结构特征
- 有且仅有一个称为开始元素的a1 ,没有直接前趋,有且仅有一个直接后继 a2;
- 有且仅有一个终结元素的an,没有直接后继,有且仅有一个直接前趋an-1;
- 其余的元素结点ai(2≤i≤n-1)都有且仅有一个直接前趋ai-1和一个直接后继ai+1;
-
基本运算
- InitList(L):构造一个空的线性表L,即表的初始化,置空表
- ListLength(L):求线性表L中的结点个数,即求表长
- GetNode(L,i):取线性表L中的第i个结点,这里要求1≤i≤ListLength(L)
- LocateNode(L,x):在 L 中查找值为 x 的结点,并返回该结点在 L 中的位置。若 L 中有多个结点的值和 x 相同,则返回首次找到的结点位置;若 L 中没有结点的值为 x ,则返回0值。
- InsertList(L,x,i):在线性表 L 的第 i 个位置上插入一个值为 x 的新结点,插入后,表 L 的长度加 1。
- DeleteList(L,i):删除线性表 L 的第 i 个结点,删除后表 L 的长度减 1。
-
例子:
【例子】:假设有两个线性表LA和LB分别表示两个几个A和B,现要求一个新集合A=A∪B。 思想:扩大线性表LA,将LB中不在LA中出现的元素插入到LA中。只要从线性表LB中依次取出每个元素,按值在线性表LA中查找,若没查到则插入之。
void union(Linear_List LA,Linear_List LB){ n=ListLength(LA); //求LA的表长 for(i=1;i<=ListLength(LB);i++){ x=GetNode(LB,i); //取LB中第i个元素赋给x if(LocateNode(LA,x)==0) //判断LA中是否有x InsertList(LA,++n,x); } }
顺序表(Sequential List)
-
定义
线性表的顺序存储指的是将线性表的数据元素按其逻辑次序依次存入一组地址连续的存储单元里,用这种方式存储的线性表称为顺序表。
# define ListSize 100 //表空间的大小应根据实际需要来定义,这里假设为100 typedef int DataType; //DataType的类型可根据实际情况而定,这里假设为int typedef struct{ DataType data[ListSize]; //数组data用来存放表结点 int length; //线性表的当前表长(实际存储元素的个数) }SeqList
-
存储位置
LOC(ai)=LOC(a1)+(i-1)*d; d:每个元素需占用d个存储单元 LOC(a1):表示为线性表的第一个元素a1的存储位置,通常称为基地址。
-
基本运算
-
插入
线性表的插入元素是指在线性表的第i-1个元素和第i个元素之间插入一个新元素x,使长度为n的线性表。
void InsertList(SeqList *L,iny i,DataType x){ int j; if(i<1 || i>L->Length+1){ printf("position error"); return; } if(L->length>=ListSize){ printf("overflow"); return; } for(j=l->Length-1;j>=i-1;j--){ L->data[j+1]=L->data[j]; //从最后一个元素开始逐一后移 L->data[i-1]=x; //插入新元素x L->length++; //实际表长加1 } }
-
删除
线性表的删除运算指的是将表中第i(1≤i≤n)个元素删除,使长度为n的线性表变为长度为n-1的线性表。
DataType DeleteList(SeqList *L,int i){ int jl DataType x; if(i<1 || i>L->Length){ printf("position error"); return; } x=L->data[i]; for (j=i;j<=L->Length;j++){ L->data[j-1]=L->data[j]; //元素前移 L->length--; //实际表长减1 return x; //返回被删除的元素 } }
链表(Linked List)
-
-
定义
链式存储结构存储线性不爱数据元素的存储空间可能是连续的,也可能是不连续的,因而链表的结点是不可能随机存储的。
单链表(线性链表)
在使用链式存储结构表示每个数据元素ai时,除了存储ai本身的信息之外,还需要一个存储指示其后继元素ai+1存储位置的指针,有这两部分组成元素ai的存储映像通常称为结点。由于这种链式表的每个结点中只包含一个指针域,因此称为单链表。
-
基本运算
-
建立单链表
- 头插法
头插法表示从一个空表开始,重复读数据,生成新结点,将读入的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止。
LinkList CreateList(){ LinkList head; ListNode *p; char ch; head=null; //置空单链表 ch=getchar(); //读入第一个字符 while(ch!='\n'){ //读入字符不是结束标志时作循环 p=(ListNode *)malloc(sizeof(ListNode)); //申请新节点 p->data=ch; //数据域赋值 p->next=head; //指针域赋值 head=p; //头指针执行新结点 ch=getchar(); //读入下一个字符 } return head; //返回链表的头指针 }
-
尾插法(不带头结点)
将新结点插入在单链表的表尾上,需增添一个尾指针rear,使其始终指向链表的尾结点。
LinkList CreateList(){ LinkList head,rear; ListNode *p; char ch; head=NULL;rear=NULL; //置空表 ch=getchar(); //读入第一个字符 while(ch!='\n'){ //读入字符2不是结束标志符时作循环 p=(ListNode *)malloc(sizeof(ListNode)); //申请新结点 p->data=ch; //数据域赋值 if(head=NULL) head=p; //新结点 *p插入空表 else rear->nexr=p; //新结点*p插入到非空表的表为结点*rear之后 rear=p; //表尾指针指向新的表尾结点 ch=getchar();//读入下一个字符 } if(rear!=NULL) rear-next=NULL; //终端结点指针域置空 return head; }
-
尾插法(带头结点)
为了简化算法,方便操作,可在链表的开始结点之前附加一个结点,并称为头结点。
LinkList CreateList(){ LinkList head=(ListNode *)malloc(sizeof(ListNode)); //申请头结点 ListNode *p,*r; DataType ch; r=head; while((ch=getchar())!='\n'){ p=(ListNode *)malloc(sizeof(ListNode)); p->data=ch; //新结点连接到尾结点之后 r->next=p; //尾指针指向新结点 r=p; //终端结点指针域置空 } r->next=NULL; return head; }
- 头插法
-
查找运算(带头结点)
- 按结点序号查找
ListNode * GetNode(LinkList head,int i){ //head为带头结点的单链表多的头指针,i为要查找的结点序号 //若查找成功,则返回查找结点的存储地址,否则返回NULL ListnNode *p;int j; p=head->next; //使p指向第一个结点 j=1; while(p!=NULL&&j<i){ //顺时针向后查找,直到p指向第i个结点或p为空为止 p=p->next; ++j; } if(j==i){ return p; }else { return NULL; } }
- 按结点值查找
ListNode * LocateNode(LinkList head,DataType k){ ListNode *p=head->next; while(p&&p->data!=k){ //循环直到p等于NULL或p->data等于k为止 p=p->next; } return p; }
-
插入运算(带头结点)
先使p指向ai-1的位置,然后生成一个数据域值为x的新结点*s,再进行插入操作。
void InsertList(LinkList head,int i,DataType x){ ListNode *p,*s; int j; p=head; j=0; while(p!=NULL&&j<i-1){ p=p->next; ++j; } if(p==NULL){ printf("ERROR\n"); return; }else{ s=(ListNode *)malloc(sizeof(ListNode)); //申请新结点 s->data=x; s->next=p->next; p->next=s; } }
-
删除运算(带头结点)
由于第i个结点的存储地址是存储在第i-1个结点的指针域next中,因此要先使p指向1第i-1个结点,然后使得p->next指向第i+1个结点,再将第i个结点释放掉。
DataType DeleteList (LinkList head,int i){ ListNode *p,*s; DataType x; int j; p=head; j=0; while(p!=NULL&&j<i-1){ //使p指向第i-1个结点 p=p->next; ++j; } if(p==NULL){ printf("位置错误\n"); exit(0); }else{ s=p->next; //s指向第i个结点 p->next=s->next; //使p->next指向第i+1个结点 x=s->data;//保存被删除结点的值 free(s); //删除第i个结点 return x; } }
循环链表(线性链表)
单链表中最后一个结点(终端结点)的指针域不为空,而是指向链表的头结点,使整个链表构成一个环。
双向链表(线性链表)
若希望从表中快速确定一个结点的直接前趋,只要在单链表的结点类型中增加一个指向其直接前趋的指针域prior即可。这样形成的链表中有两条不同方向的链,因此称为双向链表。
-