一.单链表
建表:建表的时候要记得给链表L分配一个新的空间,并让头指针指向NULL
L = (LinkList)malloc(sizeof(LinkList));
L->next = NULL;
#include <cstdio>
#include <cstdlib>
typedef struct LNode{
int data;
LNode* next;
}LNode,*LinkList; //定义结构体和结构体指针
//①头插法建立单链表
LinkList CreateList1(LinkList &L1) //这里要加& ?背住吧。
{
//首先要创建头结点,结合C对malloc的理解,返回的是一个指针,所以不是(LinkList*)
//又因为要开辟的空间就是一个LNode,而不是多少个字节,所以不是(?*sizeof(int))
L1=(LinkList)malloc(sizeof(LNode));
L1->next=NULL; //很重要指针必须初始化!!!
for(int i=0;i<7;i++)
{
LNode *p=(LNode*)malloc(sizeof(LNode));
p->data=i;
p->next=L1->next;
L1->next=p;
}
return L1; //void不行??
}
//②尾插法建立单链表
LinkList CreateList2(LinkList &L)
{
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
LNode *s,*real=L;
for(int i=0;i<7;i++)
{
s=(LNode*)malloc(sizeof(LNode));
s->data=i;
rear->next=s; //将节点s接到尾巴后面,r指向新的尾结点
rear=s;
}
rear->next=NULL; //千万要记得将尾结点的指针域置空啊
return L;
}
void PrintList(LinkList &L) //这里也加& ?
{
//LNode *p=(LNode*)malloc(sizeof(LNode));
//p=L;
LNode *p=L->next;
// while(p->next!=NULL)
while(p!=NULL)
{
//printf("%d",p.data);
printf("%d",p->data);
p=p->next;
}
}
int main()
{
//要表示一个单链表时,只需声明一个头指针L,指向单链表的第一个结点。
//LNode *L 与LinkList L1可读性更强
LinkList L1,L2; //指针初始化的工作在具体函数里面做??
CreateList1(L1);
PrintList(L1);
CreateList2(L2);
}
总结:剩下的代码看看书就可以了,都不难,就建表会建就行了。注意一下LinkList L有没有加&,
二.双链表
总结感悟:其实就是比单链表多了一个prior指针,因此增删改要稍微多一点步骤,用画图的方式会更清楚。由于没有顺序表“随机存取”的特点,要找一个东西,必须遍历。
①声明结构体,初始化
typedef struct DNode
{
int data;
struct DNode *prior,*next;
} DNode,*DLinklist;
void Init(DLinklist &L)
{
L=(DLinklist)malloc(sizeof(DNode));
L->next=NULL;
L->prior=NULL;
}
②插入
插入建议掌握尾插法,如此:头插法可以用:找到结点的前一个结点再尾插。按位序插入:遍历到位置后再尾插。
步骤:四条线四个步骤
注意判断插入的位置是否是最后一个(也就是主要判断黄色线,如果不是最后一个结点,该结点的prior应该指回e):
//头插法建立双链表
DLinkList Head_List(DLinkList &D){
D =(DLinkList)malloc(sizeof(DNode));//头节点
D->next=NULL;//空链表初始化,首尾指针都置为空
D->prior=NULL;
DNode *s;//用于指向新节点的指针
int x;
printf("请输入数据,输入9999代表结束\n");
scanf("%d",&x);
while(x!=9999){
s=(DNode*)malloc(sizeof(DNode));
s->data=x;
s->prior=D->prior;
D->prior=s;
s->next=D->next;
D->next=s;
scanf("%d",&x);
}
return D;
}
0
③删除,与插入同理:
注意:如果要销毁表,需要删除所有节点(引用DeleteNextDNode函数),最后释放头结点(free(L);L->next=NULL;//彻底销毁!!!)
看PPT上的代码也可
bool deleNode(LinkDNode *L, int index){
if (L->length == 0)
return false;
if (index < 0||index>L->length-1){
return false;
}
DNode *p = L->head;
for (int i = 0; i < index - 1;i++){
p = p->next;
}
DNode *dNode = p->next;//要删除的结点
p->next = dNode->next;
dNode->next->prior = dNode->prior;
free(dNode);
L->length--;
return true;
}
③遍历:核心是判断结点是否是表尾/头结点。除了向后遍历还可以向前遍历,还可以跳过头结点。
3.17
三、循环链表
循环单链表:设置尾指针r,当大量操作再头尾进行时比较方便,因为尾指针找到头结点的时间复杂度是O(1)
循环双链表:主要注意初始化、判断是否为空,根据定义有点不一样(看图理解)
PPT还是很清楚的,看PPT
循环双链表相比双链表来说:插入和删除不需要在多判断是否是表尾着一个条件
四.静态链表
用数组的方式实现链表,虽然连续内存,但逻辑相邻(游标确定)不一定物理相邻。
代码见PPT
总结:代码方面
①初始化的核心是如何判空。
②遍历的核心是判断结点是否是头/尾。
③表头、表中、表为插入/删除一个结点,考虑特殊情况
比较总结的那个视频没有看