链表必会知识--适合快速复习
Posted Linux bsping
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表必会知识--适合快速复习相关的知识,希望对你有一定的参考价值。
链表是数据结构中最重要的一部分内容,他可以做许多东西,低级的像一般简单的管理系统——学生管理系统,通讯录管理系统,NBA球星管理系统。高级的可以做一些实时操作系统——UCOSIII,Freertos等,他们都是用链表等数据结构做出来的。
先放个操作的图解:
一、链表的创建
链表常见的一共有四种创建方法,可以根据有无头结点、头插或者尾插来分类。(以下代码中只有有头结点尾插创建链表是我自己敲的,适配于后面的插入和删除等操作)
1、有头结点头插法创建链表
List HeadCreate(void)
{
int x;
List p; // 临时指针,用于保存声明的节点
List head; // 整个链表的头结点;
head = (List)malloc(sizeof(struct Node));
head->Next = NULL;
scanf("%d", &x);
while (x != -1) { // 当 x 等于 -1 的时候,停止创建链表
p = (List)malloc(sizeof(struct Node));
p->data = x;
p->Next = head->Next;
head->Next = p;
scanf("%d", &x); // 读取下一个节点的数据
}
return head;
}
2、无头结点头插法创建链表
List HeadCreate(void)
{
int x;
List p;
List head;
head = NULL;
scanf("%d", &x);
while (x != -1) {
p = (List)malloc(sizeof(struct Node));
p->data = x;
if (head == NULL) { // 若第一次创建节点,则将该点设置为头节点
head = p;
p->Next = NULL;
} else { // 若不是第一次创建节点,则直接将新节点接到链表头
p->Next = head;
head = p;
}
scanf("%d", &x);
}
return head;
}
3、有头结点尾插法创建链表(适用于后面的功能)
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
using namespace std;
typedef struct student
{
int data;
struct student *next;
}node;
node *create()
{
node *head, *p, *next, *s;
int x;
head = (node*)malloc(sizeof(node));
head->next=NULL;
if(!head)
{
printf("creat error\\n");
}
p = head;
scanf("%d", &x);
while (x !=999) //输入999时停止创建链表
{
s = (node *)malloc(sizeof(node));
if(!s)
{
printf("creat error\\n");
}
s->data = x;
p->next = s;
p = s;
scanf("%d", &x);
}
p->next = NULL;
return head;
}
4、无头结点尾插法创建链表
List TailCreate(void)
{
ElementType x;
List p;
List head;
List rear;
head = NULL;
rear = NULL;
scanf(%d, &x);
while (x != -1) {
p = (List)malloc(sizeof(struct Node));
p->data = x;
if (head == NULL) { // 创建链表的第一个节点
head = p;
rear = p;
p->Next = NULL;
} else {
rear->Next = p;
rear = p;
}
scanf("%d", &x);
}
rear->Next = NULL; // 链表建立结束后将最后一个节点指向 NULL(尾插法中不要遗漏)
return head;
}
二、测链表长度功能
返回值为整型,函数的参数为链表的数据类型。
int length(node *head)
{
int n = 0;
node* p;
p = head->next; //第一个是头结点,并没有数据,所以需要跨过头结点
while (p)
{
p = p->next;
n++;
}
return n;
}
测试结果正确。
三、删除一个节点
分情况讨论,需要分析删除节点的位置,可能在头部、可能在链表中间、也可能是链表尾。这里是寻找删除的数据,自己可以改成其他功能,比如输入删除哪个数才删除哪个。
node *del(node *head,int num)
{
node *p1,*p2;
p1=head->next; //跨过头结点
while(num!=p1->data && p1->next!=NULL)
{
p2=p1; //保留上一个节点,在删除最后一个节点时用到
p1=p1->next;
}
if(num==p1->data)
{
if(p1==head)
{
head->next=p1->next;
//free(p1); //下面else不带大括号就可以不用写这个free
}
else
p2->next=p1->next;
free(p1);
}
else
printf("not found!\\n");
return head;
}
由此可见删除的结果都是对的。
四、插入功能
这里和删除一样需要考虑插入的位置。
node *insert(node *head,int num)
{
node *p0,*p1,*p2;
p1=head->next;
p0=(node*)malloc(sizeof(node));
p0->data=num;
while(p0->data > p1->data&&p1->next!=NULL)
{
p2=p1;
p1=p1->next;
}
if(p0->data<=p1->data)
{
if(head->next==p1)
{
p0->next=p1;
head->next=p0;
}
else
{
p2->next=p0;
p0->next=p1;
}
}
else
{
p1->next=p0;
p0->next=NULL;
}
return head;
}
功能正确,其他位置插入可以自己尝试。
五、打印功能
没什么好说的,就是遍历一次链表。
void print(node* head)
{
node *p=head->next;
while(p)
{
printf("%5d",p->data);
p=p->next;
}
printf("\\n");
}
六、求链表中间的数
这里注意的是要判断链表的数量是奇数还是偶数。这里用到的算法是双指针,在以后的学习中很重要。
void *middle(node *head)
{
node *f,*s;
f=s=head->next;
if(head==NULL||head->next==NULL||head->next->next==NULL)
{
return 0;
}
while(f->next)
{
if(!f->next->next)
{
printf("\\n\\n%d %d\\n\\n",s->data,s->next->data);
return 0;
}
f=f->next->next;
s=s->next;
}
printf("\\n\\n%d\\n\\n",s->data);
}
由图可知两种情况都是正确的。
七、链表逆置--进阶
这里采用的是原地逆置,需要用三个指针来操作,这部分有点绕,细致讲解会在下一次文章写。这里只引入一下。
node *Reverse(node *head)
{
node *r,*p,*q;
p=head->next;
q=p->next;
p->next=NULL; //这句话就是把第一个数据节点的下一个指向NULL 必须加上
while(q)
{
r=q->next;
q->next=p;
p=q;
q=r;
}
head->next=p; //不加这句,返回的链表是个不带头结点的链表,头结点是旧链表的最后一个节点
return head;
}
答案是正确的。
下面是主函数的数据:
int main()
{
node *p;
p = create();
printf("\\n the lenth is %d\\n", length(p));
print(p);
// del(p,1);
// print(p);
// insert(p,0);
// print(p);
//翻转这里是个难点 需要考虑头结点 等一系列问题
p=Reverse(p);
print(p);
middle(p);
free(p);
return 0;
}
以上是关于链表必会知识--适合快速复习的主要内容,如果未能解决你的问题,请参考以下文章