前言
链表的基本操作思路都不难,但是函数很多,在学习的过程中也不能只敲函数,还要多联系完整版的链表,要有输出,今天就教大家写完整版的代码啦。还有我在学习链表的时候遇到的困难,也帮大家避雷啦。
一、关于链表的创建
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
int data;
struct Node*next;
}Node,*Linklist;
//Node 表示一个Node类型的指针,*Linklist则代表传入的一整个链表;
//初始化单链表(建立链表必须要做的事)
Linklist Insertlist()
{
Linklist head;
head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
//接下来用尾插法建立一个单链表,
//也可以用头插,但是头插法建立的单链表是倒序的
void creatlist(Linklist head)
{
Node* r, * s;
r = head;
int data;
while (1) {
scanf_s("%d", &data);
if (data == 0)
{
break;
}
s = (Node*)malloc(sizeof(Node));
s->data = data;
r->next = s;
r = s;
r->next = NULL;
}
}
二、单链表的应用
1.删除排序链表中的重复元素
思路:由于给定的链表的排好序的,所以重复的元素在链表中的位置是连续的,因此我们只需要对链表进行一次遍历,找到相同的元素让他指向下一个就行。
代码如下(示例):
//删除两个相同的元素
Linklist deletesome(Linklist head)
{
if (head == NULL || head->next == NULL)
return head;
Node* p = head;
while (p) {
if (p->data == p->next->data) {
p->next = p->next->next;
}
else {
p = p->next;
}
}
return head;
}
2.找到中间结点
代码如下(示例):
//找到中间节点
void middlenode(Linklist head)
{
Node* fast = head->next;
Node* slow = head->next;
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next;
}
printd("%d ",slow->data);
}
3.反转链表
思路:对于反转链表,其实有很多方法,比如直接用头插法创建链表再打印出来就可以反转,也可以改变指针的指向(后面写回文联表会用到,一般用于不带头结点的链表),这里我们介绍一种比较好理解的递归(用于带有节点的链表)。
代码如下(示例):
void daoxv(Linklist head)
{
if (head->next != NULL) {
daoxv(head->next);
}
printf("%d ", head->data);
}
4.合并两个有序链表
题目:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
思路:通过遍历链表比较连个链表的大小,重新定义指针的排序,可以直接比较,也可以用递归简化代码;
代码如下(示例):
//递归
Linklist mergeTwoLists(Linklist list1,Linklist list2)
{
if (list1 == NULL) {
return list2;
}
if (list2 == NULL) {
return list1;
}
if (list1->data >= list2->data) {
list2->next = mergeTwoLists(list1, list2->next);
return list2;
}
else {
list1->next =mergeTwoLists(list1->next, list2);
return list1;
}
}
主函数(包含以上所有函数)
int main()
{
Linklist head;
Linklist list1,list2,list3;
Linklist head;
head = Insertlist();
list1 = Insertlist();
list2 = Insertlist();
list3 = Insertlist();
creatlist(list1);
creatlist(list2);
creatlist(list3);
creatlist(head);
middlenode(head);
deletesome(head);
daoxv(head->next);
mergeTwoLists(list1,list2,list3);
print(head);
}
5.回文链表(不带头节点)
题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
思路:回文链表可以用数组,递归和快慢指针的方式,在这里我们采用快慢指针来做,加深对不带头节点的链表反转的理解。
整个流程可以分为以下五个步骤:
找到前半部分链表的尾节点。
反转后半部分链表。
判断是否回文。
恢复链表。
返回结果。
代码如下(示例):
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
int data;
struct Node* next;
}Node, * Linklist;
void Insertlist(Linklist* L)
{
*L = NULL;
}
Linklist creatlist(Linklist* L)
{
Node* s, * r;
int data;
scanf_s("%d", &data);
if (data != -1)
{
*L = (Linklist)malloc(sizeof(Node));
(*L)->next = NULL;
(*L)->data = data;
r = *L;
scanf_s("%d", &data);
while (data != -1)
{
s = (Node*)malloc(sizeof(Node));
s->data = data;
s->next = NULL;
r->next = s;
r = r->next;
scanf_s("%d", &data);
}
}
return *L;
}
bool huiwen(Linklist L)
{
if (L == NULL || L->next == NULL)
return true;
if (L->next->next == NULL) {
if (L->data == L->next->data)
return true;
else
return false;
}
Node* fast, * slow;
fast = L->next->next;
slow = L->next;
while (fast && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next;
}
Node* p, * q;
p = q = NULL;
while (L != slow) {
q = L->next;
L->next = p;
p = L;
L = q;
}
if (fast != NULL && fast->next == NULL)
{
slow = slow->next;
}
while (p != NULL)
{
if (p->data != slow->data)
return false;
p = p->next;
slow = slow->next;
}
return true;
}
int main()
{
Linklist L;
Insertlist(&L);
creatlist(&L);
if (huiwen(L)) {
printf("true\n");
}
else {
printf("false\n");
}
}