IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> C语言双向循环链表 -> 正文阅读

[数据结构与算法]C语言双向循环链表

一、介绍

在C语言中,链表分为单向链表和双向链表,单向链表只能朝一个方向遍历,中间的一个节点只能由上一个节点推出,只能往前推,无法往后推,关于单向链表的介绍可以点击单向链表介绍
这次介绍双向链表,既然叫双向就说明可以往两个方向进行遍历,可以利用中间的一个节点推出下一个节点和上一个节点,所以在定义一个双向链表节点的时候,除了要保存数据和下一个节点的地址,还得保存上一个节点的地址,定义如下:

typedef struct NODE{
	int data;//要保存的数据
	struct NODE *next;//下一节点地址
	struct NODE *prev;//上一节点地址
}*NODE;

在此基础上,如果让头结点的prev指向尾结点,让尾结点的next指向头结点,那么就构成了双向循环链表,如图示:
在这里插入图片描述

二、创建双向循环链表

思路:先创建一个节点,并且让该节点的next和prev都指向自己,让他自己跟自己循环,再依次加入新节时,这时只需改变头结点的prev和尾结点的next以及新节点的next和prev即可。
如图示:
在这里插入图片描述

示例代码:

    NODE node = GetNewNode();//获取新节点
	//先让他自己循环
	node->next = node;
	node->prev = node;
	int data;
	scanf("%d",&data);
	getchar();
	node->data = data;
	while(1){
		if(scanf("%d",&data)==1){
			getchar();
			NODE new = GetNewNode();
			new->data = data;
			InsertNode(node,new);
			ShowNode(node);
		}
		else
			break;
	}

注:在这里定义的规则是输入非数字就停止创建新节点。
给出GetNewNode函数代码:

NODE GetNewNode(){
	NODE new = malloc(sizeof(struct NODE));
	if(new==NULL){
		perror("获取内存失败!");
		exit(1);
	}else
	return new;
}

给出InsertNode函数代码:

//正向插入节点
void InsertNode(NODE node,NODE new){
	NODE temp = node;
	while(node->next != temp){//判断是否到达尾结点
		node = node->next;
	}
	node->next = new;
	new->prev = node;
	new->next = temp;
	temp->prev = new;
}

给出ShowNode函数代码:

void ShowNode(NODE node){
	NODE temp = node;
	printf("正向遍历双向循环链表:");
	while(node->next!=temp){//判断是否到达尾结点
		printf("%d\t",node->data);
		node = node->next;
	}
	printf("%d\t\n",node->data);//输出尾结点数据
}

三、排序插入

排序插入即按从小到大排列链表,创建新链表后寻找合适位置再插入,使得链表始终是按从小到大排列的。实现原理和单向链表排序插入类似,只是要多改变两个指针的指向,我的实现方法中把情况分为三类:
①插入的是第二个节点,直接插在头结点后面,如果头结点比新节点大,那么两个节点交换数据;
②中间节点,遍历链表找到两个连续节点一个节点比新节点小另一个节点比新节点大,插在中间;
③找不到比新节点大的节点,那么是最后一个节点,直接插在末尾。

如图示:
在这里插入图片描述
示例代码:

//按从小到大顺序插入
int SortInsertNode(NODE node,NODE new){
	NODE temp = node;
	if(node->next == node){//代表只有一个节点,就插到第一个节点后面
		node->next = new;
		new->prev = node;
		node->prev = new;
		new->next = node;
		if(new->data<=node->data){//如果新节点小于旧节点,则两个交换值
			int data = node->data;
			node->data = new->data;
			new->data = data;
		}
		return 1;//结束函数运行
	}else{//如果不是第一个节点
		while(node->next != temp){//开始遍历
			if(node->data<=new->data&&node->next->data>new->data){//判断某个节点是否小于新节点且该节点的下一个节点大于新节点
				//插在中间
				new->next = node->next;
				node->next->prev = new;
				node->next = new;
				new->prev = node;
				return 1;//结束运行
			}
		   node = node->next;
	    }
	}
	//能运行到这里说明新节点是最大的节点,直接插到最后
	node->next = new;
	new->prev = node;
	new->next = temp;
	temp->prev = new;
	return 1;
}

注:return无实际用途,仅仅为了结束函数运行。

四、应用

看到一道题目:
创建一个空的双向循环链表。将自然数作为链表节点里面的数据,并将若干个自然数插入链表。比如依次插入从1到10个自然数: 1,2,3,4,5,6,7、8,9,10然后通过某些操作,将其重新排列成1,3,5,7,9,10,8,6,4,2,即奇数升序偶数降序,并在屏幕上打印出来。
从中可以发现规律:奇数顺序不变,越小的偶数越扔后面,所以思路就来了:
从后往前遍历,如果遇到奇数则不变,如果遇到偶数,将节点断开插到链表末尾。
如图示:
在这里插入图片描述
示例代码:
注释较多,不再做解释。

NODE DropNode(NODE node){
	NODE first = node;//指向链表最前
	NODE q = node->prev,p;//指向链表最后,q是主指针,p是次指针
	NODE last = node->prev;//一直指向链表最后
	NODE temp = first;//最后使用
	while(q!=node){//遍历直到到达第一个节点
		if(q->data%2==0&&q!=last){//判断是否是偶数和最后一个节点,因为最后一个节点不管怎么样没必要移
			p = q;//赋值给p,不能影响主指针
			q = q->prev;//主指针向前移
			//从中断开
			p->prev->next = p->next;
			p->next->prev = p->prev;
			last->next = p;//插到最后
			p->prev = last;
			p->next = first;
			first->prev = p;
			last = p;//新插的节点成为最后的节点
		}
		else
		    q = q->prev;//主指针向前移
	}
	//判断第一个节点是否为偶数,如果是直接让第二个节点成为头结点
	if(first->data%2==0){
		temp = first->next;
	}
	return temp;//返回头结点
}

五、完整代码

//双向循环链表
#include<stdio.h>
#include<stdlib.h>
typedef struct NODE{
	int data;//要保存的数据
	struct NODE *next;//下一节点地址
	struct NODE *prev;//上一节点地址
}*NODE;
//获取新节点
NODE GetNewNode(){
	NODE new = malloc(sizeof(struct NODE));
	if(new==NULL){
		perror("获取内存失败!");
		exit(1);
	}else
	return new;
}
//正向插入节点
void InsertNode(NODE node,NODE new){
	NODE temp = node;
	while(node->next != temp){
		node = node->next;
	}
	node->next = new;
	new->prev = node;
	new->next = temp;
	temp->prev = new;
}
//按从小到大顺序插入
int SortInsertNode(NODE node,NODE new){
	NODE temp = node;
	if(node->next == node){//代表只有一个节点,就插到第一个节点后面
		node->next = new;
		new->prev = node;
		node->prev = new;
		new->next = node;
		if(new->data<=node->data){//如果新节点小于旧节点,则两个交换值
			int data = node->data;
			node->data = new->data;
			new->data = data;
		}
		return 1;//结束函数运行
	}else{//如果不是第一个节点
		while(node->next != temp){//开始遍历
			if(node->data<=new->data&&node->next->data>new->data){//判断某个节点是否小于新节点且该节点的下一个节点大于新节点
				//插在中间
				new->next = node->next;
				node->next->prev = new;
				node->next = new;
				new->prev = node;
				return 1;//结束运行
			}
		   node = node->next;
	    }
	}
	//能运行到这里说明新节点是最大的节点,直接插到最后
	node->next = new;
	new->prev = node;
	new->next = temp;
	temp->prev = new;
	return 1;
}
void ShowNode(NODE node){
	NODE temp = node;
	printf("正向遍历双向循环链表:");
	while(node->next!=temp){
		printf("%d\t",node->data);
		node = node->next;
	}
	printf("%d\t\n",node->data);
}
void ReverseShowNode(NODE node){
	NODE temp = node;
	printf("反向遍历双向循环链表:");
	while(node->prev!=temp){
		printf("%d\t",node->data);
		node = node->prev;
	}
	printf("%d\t\n",node->data);
}
NODE DropNode(NODE node){
	NODE first = node;//指向链表最前
	NODE q = node->prev,p;//指向链表最后,q是主指针,p是次指针
	NODE last = node->prev;//一直指向链表最后
	NODE temp = first;//最后使用
	while(q!=node){//遍历直到到达第一个节点
		if(q->data%2==0&&q!=last){//判断是否是偶数和最后一个节点,因为最后一个节点不管怎么样没必要移
			p = q;//赋值给p,不能影响主指针
			q = q->prev;//主指针向前移
			//从中断开
			p->prev->next = p->next;
			p->next->prev = p->prev;
			last->next = p;//插到最后
			p->prev = last;
			p->next = first;
			first->prev = p;
			last = p;//新插的节点成为最后的节点
		}
		else
		    q = q->prev;//主指针向前移
	}
	//判断第一个节点是否为偶数,如果是直接让第二个节点成为头结点
	if(first->data%2==0){
		temp = first->next;
	}
	return temp;//返回头结点
}
int main(){
	NODE node = GetNewNode();//获取新节点
	//先让他自己循环
	node->next = node;
	node->prev = node;
	int data;
	scanf("%d",&data);
	getchar();
	node->data = data;
	while(1){
		if(scanf("%d",&data)==1){
			getchar();
			NODE new = GetNewNode();
			new->data = data;
			InsertNode(node,new);//正向插入
			ShowNode(node);//输出
			ReverseShowNode(node);//反向输出
		}
		else
			break;
	}
	node = DropNode(node);//改变节点位置
	ShowNode(node);
	return 0;
}

到此结束!

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 11:34:33  更:2021-08-08 11:51:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:27:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码