数据结构2--链表基础知识
1. 链表的概念
单链表: 线性表的链接存储结构
存储思想: 用一组任意的存储单元存放线性表的元素
- 不连续
- 零散分布
单链表的存储特点:
- 逻辑次序和物理次序不一定相同
- 元素之间的逻辑关系用指针表示
例如:
单链表是由若干个结点构成
单链表的结点只有一个指针域
单链表的结点结构
代码定义
typedef struct node
{
Data data; //数据域
syruct node *next; //指针域
}Node, *Link; //Node 等价于 struct node , Link 等价于 struct node *
如何申请一个结点
p = (Link) malloc (sizeof(Node))
如何引用数据域,指针域
p->data
p->next
存储结构
头结点: 在单链表的第一个元素结点之前附设一个类型相同的结点,以便空表和非空表处理统一
头指针: 指向第一个结点的地址
尾标志: 终端结点的指针域为空
2. 单链表的实现
2.1 单链表的遍历
void displayNode(Link head)
{
p = head->next; // p指向了链表的第一个有效数据的结点
while(p != NULL)
{
printf("%d\n",p->data);
p = p->next;
}
}
2.2 求单链表的元素个数
伪算法:
-
工作指针 p 初始化,累加器 count 初始化
-
重复执行下述操作,直到 p 为空:
工作指针 p 后移
count ++ -
返回累加器 count 的值
int length(Link head)
{
p = head->next;
count = 0;
while(p != NULL)
{
p = p->next;
count++;
}
return count;
}
2.3 单链表的查找操作
bool queryNode(Link head, Data x)
{
p = head->next;
while(p!=NULL)
{
if(p->data == x)
{
printf(data);
return true;
}
p = p->next;
}
// 如果循环结束,说明没有找到
return false;
}
2.4 单链表的插入操作
让 p 移动到正确的位置
node = (Link) malloc (sizeof(Node));
node->data = x;
node->next = p->next;
p->next = node;
由于单链表带头结点,表头,表中,表尾三种情况的操作语句一致
伪算法
- 工作指针 p 初始化
- 查找第 i-1 个结点并使工作指针 p 指向该结点
- 若查找不成功,则返回 false
否则:
生成一个元素值为 x 的新结点 s
将新结点 s 插入到结点 p 之后
返回 true
bool insertNode(Link head, int i, Data x) // i 是数据 x 准备插入的位置
{
p = head; // 工作指针 p 指向头结点
count = 0;
while(p!=NULL && count n<i-1) // 查找到第 i-1 个结点
{
p = p->next;
count ++;
}
if(p == NULL)
return false;
else
{
node = (Link)malloc(sizeof(Node));
node->data = x;
node->next = p->next;
p->next = node;
return true;
}
}
2.5 创建一个单链表–头插法
头插法:将待插入结点插在头结点的后面
准备一个数组:
初始化头结点
算法描述:
head = (Link)malloc(sizeof(Node));
head->next = NULL:
插入第一个元素结点
算法描述
node = (Link)malloc(sizeof(Node));
node->data = a[0];
node->next = head->next;
head->next = node;
一次插入每一个结点
算法描述
node = (Link)malloc(sizeof(Node));
node->data = a[1];
node->next = head->next;
head->next = node;
头插法产生的顺序和数组的顺序相反。
头插法
Link newList(Data a[], int n) // n 是数组元素的个数
{
// 创建头结点
head = (Link)malloc(sizeof(Node));
head->next = NULL;
// 创建后续结点
for(i = 0; i<n; i++)
{
node = (Link)malloc(sizeof(Node));
node->data = a[i];
node->next = head->next;
head->next = node;
}
return head;
}
2.6 创建一个单链表–尾插法
将待插入的结点插在终端结点的后面
初始化
head = (Link)malloc(sizeof(Node));
head->next = NULL;
rear = head;
插入第一个元素结点
node = (Link)malloc(sizeof(Node));
node->data = a[0];
rear->next = node;
rear = node;
依次插入每一个结点
尾插法
Link newList(Data a[],int n)
{
head = (Link)mallloc(sizeof(Node)); // 生成头结点
head->next = NULL;
rear = head; //尾指针初始化
for(i = 0; i< n; i++)
{
node = (Link)malloc(sizeof(Node));
node->data = a[i];
rear->next = node;
rear = node;
}
rear->next = NULL;
return head;
}
2.7 单链表结点的删除
如何删除 p 所指向的结点?
q->next = p->next;
free(p);
查找结点过程中,如何保证 p ,q 指针一前一后?
初始化
p = head->next;
q = head;
移动指针一次
q = p;
p = p->next;
思路:
在查找过程中,如果发现 p 所指向的结点 data 值不是要找的 x ,
则 p ,q 同时后移,一旦找到,则执行删除操作
没有找到的情况:
思路:
在查找过程中,如果一直没有找到 data 域为 x 的结点,则进入上面的状态
此时,退出循环,返回 false.
删除时,注意分析边界情况(要删的表尾空表)
if(head == NULL || head->next == NULL)
return false;
bool deleteNode(Link head, Data x)
{
if(head == NULL || head->next == NULL) // 若链表没有数据
return false;
p = head->next; // 初始化。 p , q 指针一前一后
q = head;
while(p!=NULL)
{
if(p->data == x)
{
q->next = p->next;
free(p);
return true;
}
else
{
q = p;
p = p->next;
}
}
return false;
}
2.8 单链表的释放
将单链表中所有结点的存储空间释放
q =head;
head = head->next;
free(q);
3. 循环链表的实现
将单链表的首尾相连,将终端结点的指针域由空指针改为指向头结点,构成单循环链表,简称循环链表。
4. 双向链表
双向链表:在单链表的每个结点中再设置一个指向其前驱结点的指针域
结点的结构:
笔记来源
懒猫老师-C语言-链表. 致谢!