大话数据结构笔记(三)线性表
线性表:零个或多个数据元素的有限序列
强调序列和有限性
实现AUB
代码:
void union(List *La,List Lb) { int Len_la,Len_lb,i; Elemtype e; Len_la = ListLength(La); Len_lb = ListLength(Lb); for(i=1;i<=Len_lb;i++) { GetLeem(Lb,i,e); if(!LocateElem(La,e,equal)) ListInsert(La,++Len_la,e); } }
线性表的顺序存储结构:用一段地址连续的存储单元依次存储线性表的数据元素。(可用一维数组进行实现)
顺序存储结构代码:
#define MAXSIZE 20 typedef int ElemType typedef struct { ElemType data[MAXSIZE]; int length; }SqList;
数据长度与线性表长度的区别:数组长度是存放线性表空间的长度,存储分配后的这个量一般是不变的。线性表的长度是线性表中元素的个数,这个量是变化的。(任意时刻,线性表的长度应小于数组的长度)。
将存取时间性能为O(1)特点的存储结构称为随机存取结构。
将线性表中第i个元素值返回,代码:
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status//为函数类型,值为函数结果的状态代码 Status GetLem(SqList L,int i,ElemType *e) { if(L.length==0||i<1||i>L.Length) return ERROR; *e = L.data[i-1]; return OK; }
插入算法:
1>若插入位置不合理抛出异常。
2>若线性表长度大于数组长度,抛出异常。
3>从最后一个元素向前遍历到第i个位置,分别将其向后移动一个位置。
4>将要插入元素填入位置i处。
5>表长加1
Status ListInsert(SqList *L,int i,Elemtype e) { int k; if (L->length==MAXSIZE)//删除算法此处为if(L->length ==0) return ERROR; if (i<1||i>L->length) return ERROR; if(i<=L->length) { for(k=L->length-1;k>=i-1;k--) L->data[k+1]=L->data[k];//删除算法此处为L->data[k-1]=L->data[k] } L->data[i-1]=e; L->length++;//同理,L->length--; return OK; }
插入和删除的时间复杂度:O(1)元素插入到最后一个位置或删除最后一个位置,O(n)元素插入或删除第一个位置。
顺序存储结构在存,读数据时时间复杂度是O(1),插入和删除时为O(n)。
顺序结构优点:无须为表示表中元素的逻辑关系增加存储空间,可快速的存取表中任一位置的元素。
顺序结构缺点:插入和删除操作需要移动大量元素,当线性表长度变化较大时,难以确定空间的存储容量,易使空间碎片化。
线性表的链式存储结构特点是用一组任意的存储单元存储线性表的数据元素,存储单元可以连续也可以不连续。每个单元存储数据信息(的域叫信息域)和后继元素的存储地址(指针域)二者构成数据元素a1的存储映像,称为结点(Node)。
链表中第一个节点的存储位置叫头指针,线性表的最后一个节点指针为空
头指针与头结点的异同:
头指针是链表指向第一个节点的指针,若有头结点则指向头结点,头指针具有标识作用,常用头指针作为链表的名称,无论链表元素是否为空,头指针为链表的必要元素。
头节点是为了操作的统一设立其数据域一般无意义。有了头结点,在第一元素节点插入和删除节点操作和其他节点的操作相同,头节点不一定是链表的必要元素。
线性表链式存储结构(单链表)代码:
typedef struct Node { ElemType data; struct Node *next; }Node; typedef struct Node *LinkList
获得单链表第i个数据元素的算法:
1>声明一个节点p,指向链表的第一个结点,初始化j从1开始;
2>当j<1时,就遍历链表,让p的指针向后移动,不断指向下一个节点,j累加1;
3>若到链表末尾p为空,则说明第i个元素不存在。
4>否则查找成功,返回结点p的数据。
Status GetElem(LinkList L,int i,ElemType *e) { int j; LinkList p; p = L->next;//让p指向链表L的第一个节点 j=1; while(p&&j<i) { p = p->next; ++j; } if(!p||j>i) return ERROR; *e =p->data; return OK; }
单链表第一个数据插入结点的算法思路:
1>声明一个节点p指向链表的第一个节点,初始化j从1开始。
2>当j<i时,就遍历链表,让p的指针向后移动,不断指向下一节点,j累加1;
3>若到链表末尾p为空,则说明第i个元素不存在。
4>否则查找成功,在系统中生成一个空节点s。
5>将数据元素e赋给s->data;
6>单链表的插入标准语句s->next = p->next,p->next =s;
Status ListInsert(LinkList *L,int i,ElemType e) { int j; LinkList p,s; p = *L; j = 1; while(p&&j<i) { p=p->next; ++j; } if(!p||j>i) return ERROR; s = (LinkList)malloc(sizeof(Node));//生成新节点 s->data = e; s->next = p->next; p->next = s; return OK }
单链表删除元素操作代码:
Status ListDelete(LinkList *L,int i,ElemType *e) { int j; LinkList q,p; p = *L; j=1; while(p->next&&j<i) { p=p->next; ++j; } if(!(p->next)||j>i) return ERROR; q = p->next; p->next = q->next; *e = q->data; free(q); return OK; }
单链表创建思路:
1.声明一节点p和计数器i
2.生成一空链表L,将L的头结点指向NULL。
3.循环
3.1将生成的新节点赋给p.
3.2将随机生成的新数据赋给p->data
3.3将p插入到L头结点与前一新节点之间。
代码实现:
void CreateListHead(LinkList *L,int n) { LinkList p; int i; srand(time(0)); *L = (LinkList)malloc(sizeof(NODE)); (*L)->NEXT = NULL; for(i=0;i<n;i++) { p=(LinkList)malloc(sizeof(NODE)); p->data = rand()%100; p->NEXT = (*L)->NEXT; (*L)->NEXT=p; } } void CreateListEnd(LinkList *L, int n) { LinkList p; int i; srand(time(0)); *L = (LinkList)mallc(sizeof(Node)); r =*L;//r指向尾部的节点。 for(i= 0 ;i<n;++) { p = (Node *)malloc(sizeof(Node)); p->data = rand()%100; r->next = p; r = p; } r->next = NULL; }
单链表整表删除:算法思路
1.声明节点p,q
2.将第一个节点赋给p。
3.循环
3.1将下一节点赋给q,
3.2 释放p
3.3 将q赋给p
代码实现:
Status ClearList(LinkList *L) { LinkList p,q; p=(*L)->Next; while(p) { q = p->Next; free(p); p = q; } (*L)->next = NULL: return OK; }
利用数组描述的链表称为静态链表,数组中的由两个元素组成(data,cur),data相当于数据域,cur相当于链表中的p->next
线性表的静态链表存储方式: #define MAXSIZE 1000 typedef struct { ElemType Data; int cur; }component,StaticList[MAXSIZE];
未被使用的数组元素称为备用链表
将一维数组space 中各个分量连成一备用链表 Status InitList(StaticLinkList space) { int i; for(i=0;i<MAXSIZE-1;i++) space[i].cur = i+1; space[MAXSIZE-1] =0; return OK; }
静态链表的主要解决的是如何用静态模拟动态链表存储空间的分配。
若备用空间链表非空,则返回分配的节点下标 int Malloc_LL(staticLinkList space) { int i = space[0].cur; if(space[0].cur) { space[0].cur = space[i].cur; } return i; }
在L中第i个元素之前插入新的数据元素e Status ListInsert(StaticLinkList L,int i,Elemtype e) { int j,k,l; k = MAX_SIZE-1; if(i<1||i>ListLength(L)) return ERROR; j = Malloc_SSL(L); if(j) { L[j].data = e; for(l=1;l<=i-1;l++) k = L(k).cur; L[j].cur = L[k].cur; L[k].cur = j; return OK; } return ERROR; }
静态链表的删除操作:
将下标为k的空闲节点回收到备用链表 void Free_SSL(StatickLinkList space) { space[k].cur = space[0].cur; space[0].cur = k; } 删除在L中的第i个元素e status List_Delete(StaticLinkList L,int i,Elemtype e) { int j , k ; k = MAX_SIZE -1; if(i<1||i>ListLength(L)) return ERROR; for(j=1;j<=i-1;j++) k = L[k].cur; j = L[k].cur; L[k].cur = L[j].cur; Free_SSL(L,j); return OK; } 已知链表L存在,返回链表L元素的个数 int ListLength(StaticLinkList L) { int j = 0; int i = L[MAX_SIZE-1].cur; while(i) { i = L[i].cur; j++; } return j; }
循环链表解决了如何从链表当中的一个节点出发,访问到链表的全部节点
线性表的双向链表存储结构 typedef struct DulNode { ElemType data; struct DuLNode *prior; struct DuLNode * next; }DulNode , *DuLinkList;
。