单链表的操作(创建、查找、插入、删除、遍历、就地逆转等)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mhsszm/article/details/87210961

先贴原代码,后面再一一做解释。


/* 单链表的各创建等等操作

日期:2017年11月3日 21:46
*/

#include "stdafx.h"
#include <iostream>
#include "stdlib.h"
using namespace std;

//定义链表结点
typedef struct node
{
	int data;
	node * next;
}node;
/* 
创建单链表
思路:head结点,while循环输入结点,可以做个改进,head结点的data为链表的长度
日期:2017年11月3日 21:46
*/
node * create()
{
	int i = 0;
	node *head, *p, *q=NULL;
	int x = 0;
	head = new node();

	while (true)
	{
		printf("input node data:");
		scanf("%d", &x);
		if (x==0)
		{
			break;
		}
		p = new node();
		p->data = x;
		if (++i == 1)
		{
			head->next = p;
		}
		else
		{
			q->next = p;
		}
		q = p;
	}
	q->next = NULL;
	return head;
}

/*
测单链表长度
head->next 为第一个结点,循环遍历到尾结点,为空的结点为止
日期:2017年11月3日 21:53
*/
int Length(node * head)
{
	if (head == NULL)
	{
		return 0;
	}
	int len = 0;
	node *p;
	p = head->next;
	while (p != NULL)
	{
		len++;
		p = p->next;
	}
	return len;

}
/*
单链表打印

日期:2017年11月3日 21:53
*/
int Print(node * head)
{	
	int index = 0;
	node *p;
	if (head->next == NULL)
	{
		printf("link is null\n");
		return 0;
	}
	p = head->next;
	while (p != NULL)
	{
		printf ("the %dth node is %d\n", ++index, p->data);
		p = p->next;
	}
	return index;
}
/*
单链表查找(以节点数来查找,而不是以关键字来查找)

日期:2017年11月3日 21:58
*/
node * search(node*head, int pos)
{
	node *p = head->next;
	if (pos<0)
	{
		printf("incorrect position to search node!\n");
		return NULL;
	}
	if (pos == 0)
	{
		return head;
	}
	if (p == NULL)
	{
		printf("link is empty!\n");
		return NULL;
	}
	while (pos--)
	{
		if ((p=p->next)==NULL)
		{
			printf("incorrect position to search node!\n");
			break;
		}
	}
	return p;
}
/*
单链表插入节点

日期:2017年11月3日 22:10
*/
node *insert_node(node* head, int pos, int data)
{
	node * item = NULL;
	node *p;
	item = new node();
	item->data = data;
	if (pos ==0)
	{
		head->next = item;
		return head;
	}
	p = search(head, pos-1);

	//在p 与p->next中插入节点item
	//很简单  
	if (p!=NULL)
	{
		item->next = p->next;
		p->next = item;
	}
	return head;
}

/*
单链表删除节点

日期:2017年11月3日 22:10
*/
node *delete_node(node* head, int pos)
{
	node * item = NULL;
	node *p = head->next;
	
	if (p == NULL)
	{
		printf("link is empty!\n");
		return NULL;
	}
	p = search(head, pos-1);

	if (p!=NULL&&p->next!=NULL)
	{
		item = p->next;
		p->next = item->next;
		delete item;
	}
	return head;
}

/*
单链表逆转:就地逆转

日期:2017年11月3日 22:10
*/

node * reverse(node * head)
{
	node *p, *q, *r;
	//空值判断
	if (head->next == NULL)
	{
		return NULL;
	}
	p = head->next;
	q = p->next;
	p->next = NULL;
	while (q!=NULL)
	{
		r = q->next;
		q->next = p;
		p = q;
		q = r;
	}
	head->next = p;
	return head;
}

/*
查找单链表的中间元素
i走两步,j走一步……,由此来判断中间的元素
日期:2017年11月3日 22:10
*/
node * searchMidNode(node * head)
{
	int i = 0;
	int  j = 0;
	node * current = NULL;
	node * middle = NULL;

	current = middle = head->next;
	while (current!=NULL) 
	{
		if (i/2>j)
		{
			j++;
			middle = middle->next;
		}
		i++;
		current = current->next;
	}
	return middle;
}


int main()
{
	node *head = create();
	printf("------------origin list----------------------\n");
	Print(head);
	printf("len:%d\n", Length(head));
	printf("------------end origin ----------------------\n\n");

	printf("------------insert list----------------------\n");
	head = insert_node(head, 2, 5);
	printf("insert int 5 after 2th node :\n");
	Print(head);
	printf("------------end insert ----------------------\n\n");

	printf("------------delete list----------------------\n");
	head = delete_node(head, 2);
	printf("delete the 3th node :\n");
	Print(head);
	printf("------------end delete ----------------------\n\n");

	printf("------------reverse list----------------------\n");
	head = reverse(head);
	Print(head);
	printf("------------end reverse ----------------------\n\n");


	node *middle = new node();
	middle = searchMidNode(head);
	printf("middle index data:%d\n", middle->data);//t中间元素

	system("pause");

    return 0;
}



运行结果如下:
在这里插入图片描述
创建、查找、插入、删除、遍历都比较简单,看代码就能懂,重点理解下逆转。
假设连表如下:
在这里插入图片描述
逆转代码如下:

node * reverse(node * head)
{
	node *p, *q, *r;
	//空值判断
	if (head->next == NULL)
	{
		return NULL;
	}
	p = head->next;
	q = p->next;
	p->next = NULL;
	while (q!=NULL)
	{
		r = q->next;
		q->next = p;
		p = q;
		q = r;
	}
	head->next = p;
	return head;
}

先看代码3-11行:定义三个指针p,q,r,p指向第一个结点,q指向第二个结点,并将p做为尾结点。此时的链表如下:
图1
再看代码12-18行:14行,r指向q的下一个结点,见1,15行,并将p赋值给q->next,见2,16、17行进行右移操作,见3,4.
图2

在这里插入图片描述

待循环结束后,再将p赋值给head,此时链表转置完成。

在创建时,head->data还一直为空,其实位置可以用来存储链表的长度,这样会给后面的操作带来方便,比如找中间位置的值,还有计算长度就没必要线性遍历了,节省了时间,在删除与增加的时候,只要更新一下此值就好。

补充一个函数:今天看到人家单链表

/*
单链表逆转:将m到n的节点逆转,其余节点不动,1 <= m <= n <= 单链表长度
日期:2017年11月3日 22:10
*/
node * reverse(node * head,int m,int n)
{
	node *p, *q, *r,*a;
	//空值判断
	a = head;
	for (int i = 1;i<m;i++)
	{
		a = a->next;
	}	
	p = a->next;
	q = p->next;
	while (q != NULL && ++m <= n)
	{
		r = q->next;
		q->next = p;
		p = q;
		q = r;
	}	
	a->next->next = q;
	a->next = p;
	return head;
}

基本思路与逆转一致,只是要注意衔接、m=1情况的处理。由于本单链表自带head节点,遇到不带head节点的,可以先构造一个。 9-13行:将a指向m结点的前一个结点。 14-15行,指定p,q指针,用于逆转。16-22行,具体逆转与上一致,23-24行,用于衔接。网上还有别的做法,我觉得这种方法是比较好懂。

猜你喜欢

转载自blog.csdn.net/mhsszm/article/details/87210961