一、线性表
顺序存储:顺序表
链式存储:单链表,双链表,循环链表---->利用指针实现
&表示c++中的引用,如果传入的变量是指针类型的变量
在c中采用指针的指针也可以同样效果。
1、顺序表
1.1、顺序表节点定义:
顺序存储类型的定义: #define MaxSize 50//定义线性表的最大长度 typedef struct{ ElemType data[MaxSize];//顺序表的元素 int length;//顺序表的当前长度 }SeqList;//顺序表的类型定义
一维数组可以静态分配,也可以动态分配。 #define InitSize 100//表长度的初始定义 typedef struct{ ElemType *data;//指示动态分配数组的指针 int MaxSize,length;//数组的最大容量和当前个数 }SeqList
动态分配数组的C语句: L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
顺序表所占存储空间=表长*sizeof(元素的类型) 动态分配数组的C++语句: L.data=new ElemType[InitSize];
顺序表的特点:随机访问。也就是通过首地址和元素序号可以在O(1)时间访问指定的元素。 由于顺序表的逻辑上相邻的元素物理上也相邻,所以插入和删除需要移动大量元素。
1.2、顺序表基本操作的实现(复杂度O(n))
1、插入操作 在第i个位置上插入新元素e。 bool ListInsert(SeqList &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--)//将第i个元素后面的元素先进行后移 L.data[j]=L.data[j-1]; L.data[i-1]=e;//在位置i处放入e L.length++;//线性表长度加1 return true; }
1.3、删除操作(复杂度O(n))
删除L中第i个位置的元素,将被删除的元素用引用变量e返回
bool ListDelete(SeqList &L,ElemType &e) { if(i<1||i>L.length)return false; e=L.data[i-1];//将被删除的元素赋值给e for(int j=i;j<L.length;j++)//将第i个位置之后的元素前移 L.data[j-1]=L.data[j]; L.length--;//线性表长度减1 return true; }
2、单链表
单链表的节点类型定义: typedef struct LNode{ ElemType data;//数据域 struct LNode *next;//指针域 }LNode,*LinkList;
单链表是非随机存取的。因为查找某个特定的节点时,需要从表头开始遍历,依次查找。
头节点和头指针:
不管带不带头节点,头指针始终指向链表的第一个节点。而带头节点链表的第一个节点,节点不存储信息的。
无论链表是否为空,头指针始终指向头节点的非空指针。
2.1、采用头插法
将新节点s插入到当前链表的表头。也就是头节点后面
LinkList CreateList(LinkList &L) { LNode *s; int x; L=(LinkList)malloc(sizeof(LNode));//创建头节点 L->next=null;//初始为空链表 scanf("%d",&x)//输入节点的值 while(x!=999) { s=(LNode*)malloc(sizeof(LNode));创建新的节点 s->data=x; s->next=L->next; L->next=s; scanf("%d",&x); } return L; }
2.2、采用尾插法
头插法虽然建立链表简单,但是生成的链表的节点次序和输入的顺序相反。
希望输入顺序一样,可以采用尾插法。
思想:每次将新节点插入到当前链表的表尾上,所以必须要增加一个尾指针r。让它始终指向链表的尾节点。
LinkList CreateList(LinkList &L) { int x; LNode *s,*r;//r为尾指针,s指向即将插入的新节点 L=(LinkList)malloc(sizeof(LNode));//创建头节点。 scanf("%d",&x);//输入节点的值 while(x!=999) { s=(LNode*)malloc(sizeof(LNode)); s->data=x; r->next=s; r=s;//把s赋给r,此时刚插入的新节点s成了尾指针 } r->next=null;//尾指针置空 return L; }
2.3、按序号查找节点
在单链表中从第一个节点出发,顺着指针next域一一往下搜索。直到找到第i个节点为止。
LNode *GetElem(LinkList L,int i) { int flag=1;//计数,初始为1个 LNode *p=L->next;//头节点指针赋给p if(i==0)return L;//如果i=0,返回头节点 if(i<1)return null;//如果i无效,则返回null while(p&&j<i)//从第1个节点开始找,查找第i个节点 { p=p->next; j++ } return p;//返回第i个节点的指针。 } //复杂度为O(n)
2.4、按值来查找节点
从单链表中第一个节点处出发,从前往后比较表中节点数据域的值,然后返回这个节点的指针。
LNode *LocateElem(LinkList L,ElemType e) { LNode *p=L->next; while(p!=null&&p->data!=e)//从第一个节点开始找data域为e的节点 p=p->next; return p;//找到后返回该节点指针。 }
2.5、插入节点操作
插入节点时将值为x的新节点插入到单链表的第i个位置上。所以插入之前要检查位置的合法性。
然后在找到它的前驱,也就是第i-1个节点,然后进行插入操作。
所以首先调用方法GetElem(L,i-1);获取到i-1位置的指针
LNode *p=GetElem(L,i-1); s->next=p->next;//先后连 p->next=s;//在前连 //插入操作复杂度为O(1)
2.6、删除节点操作
删除第i个节点。同理还是检查位置的合法性,然后查找第i-1个位置节点
也即是它的前驱,然后删除
所以还是调用方法GetElem(L,i-1);
LNode *p=GetElem(L,i-1); LNode *s; s=p->next;//让s指针指向打算删除的节点 p->nexts->next;//将s节点断开。 free(s);//释放节点空间
3、双链表
3.1、节点类型定义:
typedef struct DNode { ElemType data;//数据域 struct DNode *prior,*next;//前驱和后继指针 }DNode,*DLinkList;
3.2、双链表的插入操作:
在p节点之后插入新节点s
//核心代码 s->next=p->next; p->next->prior=s; s->prior=p; p->next=s;
3.2、双链表的删除操作
p->next=p->next->next; p->next->next->prior=p; free(p->next); 或者令s=p->next; p->next=s->next; s->next->prior=p; free(s);