链表-C++

1.链表的介绍
//链表的意义:减少不必要的空间浪费+不占用连续的一片内存空间
a.链表的结构:链表结构由若干环节组成,每个环节称为一个结点或节点(C++中链表的环节用结构体变量实现),结点内部空间是连续的,结点与结点间的存储空间不一定需要连续,通过指针建立结点之间的链接,由一个结点找到另一个结点。
每一结点的结构eg.

typedef struct
{
    int age;
    char n[10];
    STU *next;  //指向下一结点的指针
}STU;

b.链表的类型:
单向链表(简称单链表)
//单链表又分为
//不带头结点的单链表:所有结点都存储数据序列中的数据
//带头结点的单链表:从第2个结点开始存储数据带头结点的链表可以使操作变得简单
双向链表
单向循环链表
双向循环链表等

//单链表实现:有一个头指针(首指针)指向头结点/首元结点,每个结点中有包含指向下一结点的指针,最后一个结点的值为NULL(即空指针,不指向任一结点,链表结束)。

//不带头结点的单链表 VS 带头结点的单链表
不带头结点的单链表:直接由一个头指针指向首元结点。
带头结点的单链表:头指针指向头结点,头结点内包含的指针指向首元结点。
c.链表结构的优点:插入、删除结点数据时,链表结构的算法效率比较高(只需改变指针指向)。

d.链表结构的缺点:指针占用额外的存储空间;结点本身的存储空间需要动态申请和释放。

//在解决实际问题时,需依据长期编程积累的经验来决定使用哪一种数据结构较为有利

  1. 习惯动态申请空间的写法
    动态空间只能用指针间接访问

a.

int *p;
p = new int;
delete p;

b.

int *p;
p = new int(8);
delete p;

c.

int *p;
p = new int[10];
delete []p;
delete [10]p;

d.

int (*p)[4];
p = new int[3][4];
delete []p;
delete [3]p;

3.习惯const的写法

a.

//const型变量 
const float pi = 3.14;
float const pi = 3.14;

b.

//指针常量 
int * const p = &a;

c.

//指向常量的指针 
const int *p; 

//指针作为函数参数:在调用时,是传值调用,传递的是地址值,系统需要分配内存空间给形参变量
//setw() 默认向右对齐,左侧填充空格

4.链表的常用算法 [注:这里的链表指不带头结点的单链表]

a.首先,定义结构体类型:

struct node    //定义结构体类型,只能这么定义,不能用typedef 
{
	int data;
	node *next;
};

重点理解:要改变链表中某结点内指针的指向,要访问next 并修改它

b.创建无序链表

//返回链表的头指针//学习迭代思想
node *create() 
{
	cout << "正在创建一条无序链表" << endl; //调试语句 
	node *p1, *p2 = NULL, *head = NULL;
	int a;
	cin >> a;
	while(a <= 0)//前提:假设输入的数都为正整数,当输入的数为非正整数时,则结束创建结点。//应具体问题具体分析。
	{
		p1 = new node; //迭代:申请一个结点 
		p1->data = a;  //赋值结构体内的变量数值 
		p1->next = NULL;
		if(head == NULL)
			head = p1;
		else
			p2->next = p1;//迭代:此时的 p2 指上一结点 //学习 
		p2 = p1;//p2存为副本
		cin >> a;	
	}
	return head; 
}

c.创建有序链表
—有序链表:一个链表中各结点的数据域值的大小从头到尾依次递增或递减。依次递增的为升序链表;依次递减的为降序链表。注意空链表是升序链表。
—创建有序链表可采用的方法是:循环输入数值,建立一个新结点,其数据域就是输入的数值,调用插入结点函数,将该新结点插入到链表中,使链表始终保持升序。

//返回链表头指针 
node *create_sort()
{
	 cout << "正在创建一条升序链表" << endl; //调试语句,输出了,表示调用成功 
	node *p, *head = NULL;
	int a;
	cin >> a;
	while(a != -1)
	{
		p = new node;
		p->data = a;
		head = insert(head, p);
		cin >> a;
	}
	return head;
}

d.插入一个结点(本节算法适用于升序链表)
—算法思路:插入结点时,若原链表为空链表,则构造一个具有一个结点的链表,首结点指向该结点即可。若原链表是非空链表,则插入结点时分三种情况:1.插入原链表首结点之前;2.插入在链表中间;3.插入在链表尾结点之后。

//插入结点;返回头指针 //向一个升序链表中插入一个新结点,结果链表仍然是升序的 
node *insert(node *head, node *p)
{
	node *p1 = head, *p2 = NULL;
	if(head == NULL)//当为空表时 
	{
		head = p;
		p->next = NULL;
		return head;
	}
	else //如果原链表不是空表,则 先找插入位置;再修改指针的指向 
	{//算法:从头到尾找下去;当找到比p->data大的结点(这个结点)时,停止,把p插入到这个结点的前面(把这个结点前面的结点next指向p,把p的next指向这个结点) 
		while(p1 != NULL)
		{
			if(p1->data >= p->data)//当找到了这个结点时 
			{
				if(p1 == head)//这个结点是头结点
				{
					head = p;
					p->next = p1;
					return head;
				}
				else//如果为中间结点 
				{
					p2->next = p;
					p->next = p1;
					return head;
				} 
			}
			else
			{
				p2 = p1;//p2做副本 
				p1 = p1->next;	
			}
		}
		p2->next = p;//插入末尾 
		p->next = NULL;
		return head;
	}
}

e.遍历链表(查找结点)
—含义:依次访问链表的各个结点。
—应用:依次输出各结点数据值;在链表中查找某个结点是否存在.
—使用const:一方面保护指针所指向的值; 另一方面保护查找到的结点。

//打印链表
void print(const node *p) //需要 头指针 参数 
{//打印各结点数据值 
	cout << "打印链表" << endl;
	while(p != NULL)
	{
		cout << setw(4) << (p->data);
		p = p->next;//迭代 
	}
	cout << endl;
}
//查找第一个值为num的结点位置 //找到了:返回num的结点位置;找不到:返回NULL
node *search(node *head, int num)
{
	cout << "正在查找值为" << num << "的结点的位置" << endl; //调试语句
	node *p = head;
	while(p != NULL)
	{
		if(p->data == num)
			return p;
		p = p->next;
	}
	return NULL;//返回空指针:用 return NULL; 即可
}

f.删除一个结点
—删除结点的含义:删除链表中满足一定条件的结点。当有多个符合删除条件结点,则只删除第一个。
—head为空—无需删除
head为非空 —找不到待删除结点 无须删除
找到待删除结点—待删除结点为首结点,删后要重新设置首指针head
待删除结点为其他结点,此时不需要修改首指针,但要修改被删除的结点的上一结点的next指针指向。

//删除第一个值为num的结点;返回删除后链表的头指针; 
node *delete_one_node(node *head, int num)
{
	cout << "正在删除值为" << num << "的第一个结点" << endl;//调试语句
	node *p1 = head;//p1遍历 
	node *p2 = NULL;
	while(p1 != NULL)
	{
		if(p1->data == num)//先找:找到了 
		{
			if(p2 == NULL)//说明p1是头指针
			{
				head = p1->next;
				delete p1;
				return head; 	
			}
			else
			{
				p2->next = p1->next;//算法,当找到时,先把前一结点的next值更新为当前结点的next值,然后删除当前结点
				delete p1;
				return head;
			}
		}
		else //找不到
		{
			p2 = p1;//记住上一结点 
			p1 = p1->next;	
		} 
	}
	return head;
}

g.释放链表
—含义:释放链表全体结点空间。
—算法思路:一个一个删

//释放链表 
void delete_chain(node *head)
{
	cout << "正在释放链表" << endl;//调试语句 
	node *p1 = head, *p2 = NULL;
	while(p1 != NULL)
	{
		p2 = p1;//p2做副本
		p1 = p1->next;
		delete p2;//删除p2所指的内存空间
	}
}

h.主函数样例:

#include <iostream>
#include <iomanip>
using namespace std;

......//各类函数

int main()
{
	node *head;
	int num;
	head = create(); //创建一条无序链表
	cout << "完成" << endl;
	print(head);     //打印创建好的链表
	cout << "输入待删除结点上的整数:";
	cin >> num;
	head = delete_one_node(head, num); //删除结点 
	print(head); //打印删除一个结点后的链表
	cout << "输入要查找的整数:";
	cin >> num;
	if(search(head, num) != NULL) //查找 
		cout << num << "在链表中" << endl;
	else
		cout << num << "不在链表中" << endl;
	delete_chain(head); //释放无序链表空间
	cout << "释放了无序链表" << endl;//改进
	cout << "---------------" << endl; 
	head = create_sort();//创建一条升序链表 
	cout << "完成" << endl;
	print(head); //打印创建好的升序链表
	delete_chain(head); //释放升序链表空间
	cout << "释放了升序链表" << endl;
	return 0; 
}

欢迎大家在下方积极留言(✪ω✪)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值