链表必会知识--适合快速复习

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;
}

以上是关于链表必会知识--适合快速复习的主要内容,如果未能解决你的问题,请参考以下文章

RTOS训练营课程学习方法和结构体知识复习 + 链表知识

RTOS训练营课程学习方法和结构体知识复习 + 链表知识

暑假学习第七周

Java容器(ListSetMap)知识点快速复习手册(下)

Dubbo必知必会的知识点

数据结构和算法学编程必知必会的50个代码实现,你都会了吗?