上篇文章介绍了顺序表的一些操作,不知道各位有没有发现了顺序表的一些缺点,比如
- 插入删除操作所需要移动的元素很多,浪费算力。
- 必须为数组开足够的空间,否则有溢出风险。
引入另一种线性结构引入链表可以解决顺序表的这一些缺点。
链表是一种常见的数据结构,它由一系列的节点构成,每个节点包含一个值和一个指向下一个节点的指针。链表的基本结构如下图所示:
每个节点之间在物理上并不是连续的,逻辑上连续。
链表主要有单链表,双链表,循环链表
本文介绍单链表的有关操作
//定义结点类型
typedef struct Node {
int data; //单链表数据域
struct Node *next; //单链表的指针域
} Node;
要实现的功能
Node*Init();
//创建一个结点
Node* CreateNode(DateType x);
//打印or遍历 单链表
void Print(Node* head);
//销毁单链表
void Destory(Node* head);
//单链表头插创建链表
Node*HeadInsert();
//单链表尾插创建链表
Node*TailInsert();
//获得链表长度
int GetLenth(Node*head);
//单链表结点查找,按值
Node* Find_date(Node* head, DateType x);
//单链表结点查找,按位
Node* Find_pos(Node* head, int pos);
//单链表插入,成为第i个节点
void Insert(Node*head,int pos,DateType x);
//单链表结点删除(删除pos位置的结点)
void Erase(Node*head, int pos);
初始化链表
初始化主要完成以下工作:创建一个单链表的头节点
有了头节点后逐步向头节点后面添加节点就可以了
Node* Init()
{
Node* Head;
Head = (Node*)malloc(sizeof(Node));
if (Head == NULL) {
perror("malloc:");
return;
}
Head->next = NULL;
return Head;
}
创建一个节点
Node* CreateNode(DateType x)
{
Node* newNode = malloc(sizeof(Node));
newNode->Date = x;
newNode->next = NULL;
return newNode;
}
遍历单链表
声明一个指针curNode,从头结点指向的第一个结点开始,如果curNode不为空,那么就输出当前结点的值,并将curNode指向下一个结点,直到遍历到最后一个结点为止。
void Print(Node* head)
{
Node* curNode = head->next;
while (curNode)
{
printf("%4d", curNode->data);
curNode = curNode->next;
}
printf("\n");
}
销毁单链表
可不能直接free(head),因为链表在物理结构上是不连续存储的,把表头销毁之后就找不到其他节点了,所以必须要把表头留着,销毁链表必须要一个结点一个结点去销毁(表头要移动)!!最后不要忘记把head置为NULL。
void Destory(Node* head)
{
Node* curNode = head;
while (curNode)
{
head = curNode->next;
free(curNode);
curNode = head;
}
head = NULL;
}
头插法创建单链表
Node*HeadInsert()
{
Node* head = Init();
DateType x;
while (scanf("%d", &x) != EOF)
{
Node* insertNode = CreateNode(x);
insertNode->next = head->next;
head->next = insertNode;
}
return head;
}
测试结果
EOF通过ctrl+z实现,可能要多输入几次
尾插法创建单链表
Node*TailInsert()
{
Node* head=Init();
Node* pNode = head; //pNode始终指向终端结点,开始时指向头结点
int x;
while (scanf("%d", &x) != EOF)
{
Node* insertNode = CreateNode(x);
pNode->next = insertNode;
pNode= insertNode;
}
pNode->next = NULL;
return head;
}
测试结果
求单链表长度
求链表长度不算表头
//求单链表的长度
int GetLength(Node*head){
Node *p = head->next;
int len = 0;
while(p){
len++;
p = p->next;
}
return len;
}
查找
按值查找
Node* Find_date(Node* head, DateType x)
{
Node* p = head->next;
while (p && p->data != x) {
p = p->next;
}
return p;
}
按位查找
头结点算0
Node* Find_pos(Node* head, int pos)
{
int i = 1;
Node* p = head->next;
if (pos == 0)
return head;
if (pos < 1)
return NULL;
while (p && i < pos) {
p = p->next;
i++;
}
return p; //如果pos大于表长,p=NULL,直接返回p即可
}
插入
这里所说的插入是将值为x的新结点插入到单链表的第pos个位置上(pos是从0开始计数的)。(不包括头结点)
从表头开始遍历,查找第 i-1个结点,即插入位置的前驱结点p,然后令新结点s的指针域指向p的后继结点,再令结点p的指针域指向新结点s。
void Insert(Node* head, int pos, DateType x)
{
Node* p = Find_pos(head, pos - 1);
Node* s = CreateNode(x);
s->next = p->next;
p->next = s;
}
测试结果
删除
void Erase(Node* head, int pos)
{
Node* p = Find_pos(head,pos - 1);
Node* eNode = p->next;
p->next = Find_pos(head,pos + 1);
free(eNode);
}
测试结果