用通俗的语言来说,单链表就像是头和尾没有连接起来的自行车链,每一个节点都是独立的,却又相互联系。特点:地址不连续,但是又相互有联系。
1.头插法:
(1)为了操作方便,一般情况下,链表的头节点是不存放数据的。
(2)如果输入1 2 3 4,那么输出是 4 3 2 1,是相反的。从这里我们就有了头插法的思路,首先让头节点(head)和第一个新的节点连接(temp1)。之后的新节点(temp2)要插入到第一个新节点(temp1)的前面。在图中的表现如下:
第一步:让头指针和第一个temp连接起来:
第二步:第二个temp连接到第一个temp的前面:
第三步:头节点和第二个temp连接(头连temp):
最后一步就是头节点和第一个temp断开:
后续就以此类推了。
代码如下:
temp->next = head->next;
head->next = temp;
2.尾插法:
(1)头节点依旧不存放数据
(2)如果输入 1 2 3 4,那么就输出 1 2 3 4,思路:需要一个节点指针tail,可以理解为游标,防止头指针丢失的。一开始只有一个节点的时候,链表的头和尾是一样的。所以一开始会有tail=head。
第一步:头和尾指向同一个位置:
第二步:尾和新节点temp连接:
后续就以此类推了
代码如下:
tail->next = temp;/*先连接两个节点*/
tail = tail->next;/*尾指针后移*/
当你能看懂创建链表的时候,后面的基本操作基本也都会了,要说的也就是删除这里了。
原理:找到要删除的位置的节点的前一个节点,然后让删除位置前一个节点的next指针指向它的下下个节点,接着释放掉删除位置的节点。这就完成了删除操作。
图解:
假如我要删除第2个节点,那么我就要用一个指针遍历到第1个节点:
这个时候节点1和节点3直接连接起来:
最后释放掉节点2,这就相当于本来你和你女朋友拉着手呢,结果来了另一个比你帅比你有钱的男孩,拉走了你的女朋友,然后你被抛弃了哈哈哈。
代码如下:
/*
Function name :deleteNode
Description : 指定位置删除节点
Parameter :
@head : 单链表的头指针
@i : 要删除的位置
return : 返回1成功,返回其他失败
*/
int deleteNode(Node *head, int i)
{
Node *temp = head;/*防止头指针丢失*/
int num = getNum(head);/*获取链表长度*/
if (i<1 || i>num) return 0;/*删除位置不合法*/
for (int k = 0; k < i - 1; k++)/*temp指针移到要删除的节点前*/
{
temp = temp->next;
}
Node *willDelete = temp->next;/*要删除的节点*/
temp->next = willDelete->next;/*隔着要删除的节点连接*/
free(willDelete);/*释放要删除的节点*/
return 1;
}
讲在前面,如果你了解单链表的基本原理,那么这个代码看起来会很轻松。
/*********************************************
@File name : singleChainList.c
@Author : StudyCcYa
@Version : 1.0
@Date : 2020-11-08
@Description : 用c语言实现数据结构——单链表
********************************************/
#include
#include
#include
typedef int Type;
/*单链表节点的结构体*/
typedef struct Node
{
Type data;/*单链表的数据*/
struct Node *next;/*用于指向下一个节点的结构体指针*/
}Node;
Node *initNode();/*初始化单链表节点*/
Node *emptyNode();/*创建一个空的节点*/
Node *creatListHead();/*头插法创建一个单链表*/
Node *creatListTail();/*尾插法创建一个单链表*/
int printList(Node *head);/*输出单链表中的所有节点*/
int getNum(Node *head);/*统计节点个数*/
int insertElem(Node *head, int i,Type elem);/*指定位置增加(插入)数据*/
int deleteNode(Node *head, int i);/*指定位置删除节点*/
Type getNode(Node *head, int i);/*查找指定位置的数据*/
int changeNode(Node *head, int i,Type elem);/*修改指定位置的数据*/
/*
Function name : initNode
Description : 初始化单链表节点
Parameter :
无
return : 返回一个单链表节点,否则失败
*/
Node *initNode()
{
Node *temp = (Node *)malloc(sizeof(Node));/*申请一个节点的空间*/
assert(temp);/*断言,如果申请空间失败则报错*/
temp->data = 0;
temp->next = NULL;
return temp;
}
/*
Function name : emptyNode
Description : 创建一个空的节点
Parameter :
无
return : 返回一个空的单链表节点,否则失败
*/
Node *emptyNode()
{
Node *temp = (Node *)malloc(sizeof(Node));/*申请一个节点的空间*/
assert(temp);
temp->next = NULL;
return temp;
}
/*
Function name : creatList
Description : 头插法创建一个单链表
Parameter :
无
return : 返回一个单链表的头指针,其他失败
*/
Node *creatListHead()
{
int n;
Type elem;
Node *head = emptyNode();/*头指针不存放数据*/
printf("请输入要创建链表节点的数量(头插):\n");
scanf("%d",&n);
printf("请输入数据:\n");
for (int i = 0; i < n; i++)
{
Node *temp = initNode();
scanf("%d",&temp->data);
temp->next = head->next;/*新节点的next指针和头节点的next的指针指向同一位置*/
head->next = temp;/*连接*/
}
return head;
}
/*
Function name : creatListTail
Description : 尾插法创建单链表
Parameter :
@head : 单链表的头指针
return : 返回一个头指针,返回其他失败
*/
Node *creatListTail()
{
Node *head = emptyNode();/*头指针不存放数据*/
Node *tail = head;/*一开始头尾指向位置一样*/
int n;
printf("请输入要创建链表节点的数量(尾插):\n");
scanf("%d", &n);
printf("请输入数据:\n");
for (int i = 0; i < n; i++)
{
Node *temp = initNode();
scanf("%d", &temp->data);
tail->next = temp;/*先连接两个节点*/
tail = tail->next;/*尾指针后移*/
}
return head;
}
/*
Function name : printList
Description : 输出单链表
Parameter :
@head : 单链表的头指针
return : 返回1成功,返回其他失败
*/
int printList(Node *head)
{
Node *temp = head->next;/*临时链表指针,防止头指针丢失*/
for (; temp != NULL; temp = temp->next)/*循环遍历链表*/
{
if (temp->next != NULL)/*没有到最后一个节点*/
{
printf("%d->", temp->data);
}
if (temp->next == NULL)/*到最后一个节点*/
{
printf("%d\n",temp->data);
return 1;/*输出成功*/
}
}
return 0;
}
/*
Function name : getNum
Description : 统计节点个数
Parameter :
@head : 单链表的头指针
return : 返回单链表节点的个数,返回其他失败
*/
int getNum(Node *head)
{
int i = 0;/*用于计数*/
Node *temp = head->next;/*第一个节点不算*/
while (temp!=NULL)
{
i++;
temp = temp->next;
}
return i;
}
/*
Function name :insertElem
Description : 指定位置增加(插入)数据
Parameter :
@head: 单链表的头指针
@i : 插入的位置
@elem: 插入的数据
return : 返回1成功,其他失败
*/
int insertElem(Node *head, int i,Type elem)
{
Node *temp = head;
int num = getNum(head);/*获取单链表的长度*/
if (i<1 || i>num+1) return 0;/*插入位置不合法*/
//if (i == 1) /*如果在最前面插入*/
//{
// Node *newNode = initNode();
// newNode->data = elem;//赋值
// newNode->next = temp->next;/*这里相当于一个头插*/
// temp->next = newNode;
// return 1;
//}
//if (i >= 1 && i <= num)/*如果在中间插入*/
//{
for (int k = 0; k < i-1; k++)/*temp指针移到要插入的节点前,这个时候就体现出头节点不保存数据的好处了*/
{
temp = temp->next;
}
/*下面的代码段就跟上面一样了*/
Node *newNode = initNode();
newNode->data = elem;
newNode->next = temp->next;
temp->next = newNode;
return 1;
//}
return 0; /*表示插入失败咯*/
/*插到最后的情况是跟中间一样的。所以不再做处理*/
}
/*
Function name :deleteNode
Description : 指定位置删除节点
Parameter :
@head : 单链表的头指针
@i : 要删除的位置
return : 返回1成功,返回其他失败
*/
int deleteNode(Node *head, int i)
{
Node *temp = head;/*防止头指针丢失*/
int num = getNum(head);/*获取链表长度*/
if (i<1 || i>num) return 0;/*删除位置不合法*/
for (int k = 0; k < i - 1; k++)/*temp指针移到要删除的节点前*/
{
temp = temp->next;
}
Node *willDelete = temp->next;/*要删除的节点*/
temp->next = willDelete->next;/*隔着要删除的节点连接*/
free(willDelete);/*释放要删除的节点*/
return 1;
}
/*
Function name : getNode
Description : 查找指定位置的数据
Parameter :
@head : 单链表的头指针
@i : 要查找的位置
return : 返回1成功,返回其他失败
*/
Type getNode(Node *head, int i)
{
Node *temp = head;/*防止头指针丢失*/
int num = getNum(head);/*获取单链表长度*/
if (i<1 || i>num) return 0;/*查找位置不合法*/
for (int k = 0; k < i; k++)/*temp指针移动到要查找的节点*/
{
temp = temp->next;
}
return temp->data;/*将数据返回到函数*/
}
/*
Function name : changeNode
Description : 修改指定位置的数据
Parameter :
@head : 单链表的头指针
@i : 修改的位置
@elem : 替换的数据
return : 返回1成功,其他失败
*/
int changeNode(Node *head, int i, Type elem)
{
Node *temp = head;/*防止头指针丢失*/
int num = getNum(head);/*获取单链表长度*/
if (i<1 || i>num) return 0;/*修改位置不合法*/
for (int k = 0; k < i; k++)/*temp指针移动到要修改的节点*/
{
temp = temp->next;
}
temp->data = elem;/*替换数据*/
return 1;
}
int main()
{
Node *head = creatListHead();/*头插法*/
printList(head);/*输出头插链表*/
Node *head1 = creatListTail();/*尾插法*/
printList(head1);/*输出尾插链表*/
printf("增:\n");
insertElem(head, 1, 66);/*第1个位置插入66*/
printList(head);
insertElem(head, 3, 77);/*中间位置插入77*/
printList(head);
insertElem(head, 8, 99);/*最后位置插入99*/
printList(head);
printf("删:\n");
deleteNode(head, 1);/*删除第一个节点*/
printList(head);
deleteNode(head, 3);/*删除中间节点*/
printList(head);
deleteNode(head, 6);/*删除最后节点*/
printList(head);
printf("查:\n");
printf("1:%d\n", getNode(head, 1));/*查询第1个节点的数据*/
printf("2:%d\n", getNode(head, 2));/*查询中间节点的数据*/
printf("5:%d\n", getNode(head, 5));/*查询最后节点的数据*/
printf("改:\n");
changeNode(head, 1, 22);/*第1个节点数据改成22*/
printList(head);
changeNode(head, 3, 33);/*中间节点数据改成33*/
printList(head);
changeNode(head, 5, 44);/*最后节点数据改成44*/
printList(head);
return 0;
}
如有不足和建议,欢迎指正和讨论。后续会将所有的数据结构用c语言和java语言实现。