c/c++ 线性表之双向循环链表

线性表之双向循环链表

不是存放在连续的内存空间,链表中的每个节点的next都指向下一个节点,每个节点的before都指向前一个节点,最后一个节点的下一个节点不是NULL,是头节点。

真实的第一个节点是头节点,头节点不存放数据,单纯为了编写程序方便。但是下面注释里写的【第一个节点】的含义是头节点的下一节点,也就是真实存放数据的第一个节点。

下面的代码实现了以下功能

函数 功能描述
push_back 从链表的最后插入节点
push_front 从链表的起始插入节点
show_list 打印出链表里每个节点的值
pop_back 删除链表最后一个节点
pop_front 删除链表起始节点
insert_val 在合适的位置插入一个节点;
比如原来的链表:1->3->NULL,当要插入的节点的值为2的时候,就会在1和3之间插入这个节点,插入后的链表:1->2->3->NULL
find 查找指定的节点
length 返回链表中节点的个数
delete_val 删除指定的节点
sort 排序,重新排列节点
resver 按倒序,重新排列节点
clear 释放除了头节点之外的所有节点所占用的内存空间
destroy 释放所有节点的所占用的内存空间,包括头节点

whileshuangnode.h

#ifndef __WHILESHUANGNODE__
#define __WHILESHUANGNODE__ #include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <memory.h>
#include <stdbool.h> #define ElemType int typedef struct Node{
ElemType data;
struct Node* before;
struct Node* next;
}Node; typedef struct NodeList{
Node* first;
Node* last;
size_t size;
}NodeList; void init(NodeList*);
void push_back(NodeList*, ElemType);
void push_front(NodeList*, ElemType);
void pop_back(NodeList*);
void pop_front(NodeList*);
void show_list(NodeList*);
void insert_val(NodeList*, ElemType);
Node* find(NodeList*, ElemType);
void delete_val(NodeList*, ElemType);
void sort(NodeList*);
void sort1(NodeList*);
void resver(NodeList*);
void resver1(NodeList*);
void resver2(NodeList*);
void clear(NodeList*);
void destroy(NodeList*); #endif

whileshuangnode.c

#include "whileshuangnode.h"

void init(NodeList* list){
list->first = (Node*)malloc(sizeof(Node));
list->last = list->first;
list->first->before = list->last;
list->last->next = list->first;
list->size = 0;
} Node* create_node(ElemType val){
Node* node = (Node*)malloc(sizeof(Node));
assert(NULL != node);
node->data = val;
node->before = NULL;
node->next = NULL;
return node;
}
void push_back(NodeList* list, ElemType val){
Node* p = create_node(val); p->before = list->last;
p->next = list->first; list->first->before = p;
list->last->next = p; list->last = p; list->size++;
} void push_front(NodeList* list, ElemType val){
Node* p = create_node(val); //设置p的before和next
p->before = list->first; //第一次添加节点的时候要移动未指针,还要设置first的before
if(list->first == list->first->next){
list->last = p;
p->next = list->first;
list->first->before = p;
}
//不是第一次添加节点的时候,要把原第一个节点的before指向,新添加的节点
else{
p->next = list->first->next;
list->first->next->before = p;
}
//设置头指针的next节点
list->first->next = p; list->size++;
} void show_list(NodeList* list){
Node* tmp = list->first->next;
while(tmp != list->first){
printf("%d->", tmp->data);
tmp = tmp->next;
}
printf("NULL\n");
} void pop_back(NodeList* list){
if(list->size == 0)return; free(list->last);
//让尾指针的next指向NULL
list->last->before->next = list->first;
//让first的before指向新的尾节点
list->first->before = list->last->before;
//让尾指针指向原尾节点的前一个节点
list->last = list->last->before; list->size--;
}
void pop_front(NodeList* list){
if(list->size == 0)return; free(list->first->next);
//就剩一个节点的时候,要移动尾指针。
if(list->first->next == list->last){
list->last = list->first;
list->last->next = list->first;
list->first->before = list->last;
list->size--;
return;
} //头指针的next指向第二个节点
list->first->next = list->first->next->next;
//第二个节点的before指向头节点
list->first->next->before = list->first; list->size--;
}
void insert_val(NodeList* list, ElemType val){
Node* n = create_node(val);; Node* p = list->first;
while(p->next != list->first && val > p->next->data){
p = p->next;
}
//第一次加节点,或者,最后一个节点的值也没有比给的值大的时候
if(list->first == p->next){
n->next = list->first;
n->before = list->last;
list->last->next = n;
list->first->before = n;
list->last = n;
list->size++;
return;
}
//新节点的next指向原节点的下一个节点
n->next = p->next;
//原节点的next指向新节点,注意这句的位置必须在上句的下面
p->next = n;
//新节点的下一个节点的before指向新节点
n->next->before = n;
//新节点的before指向原节点
n->before = p; list->size++;
}
//寻找给定值的节点的位置
Node* find(NodeList* list, ElemType val){
if(list->size == 0)return NULL; Node* p = list->first;
while(p->next != list->first && p->next->data != val){
p = p->next;
}
if(list->first == p->next){
return NULL;
}
printf("%d is found\n", p->next->data);
return p->next;
}
void delete_val(NodeList* list, ElemType val){
Node* p = find(list, val);
if(NULL == p) return; //删除的节点是尾节点的时候,要移动last
if(p == list->last){
list->last = p->before;
p->before->next = list->first;
list->first->before = p->before;
list->size--;
return;
} p->before->next = p->next;
p->next->before = p->before; free(p); list->size--;
} void sort(NodeList* list){
if(list->size == 1 || list->size == 0)return; //p为第一个节点
Node* p = list->first->next; //t是空白list,往t里加节点
Node* t = list->first;
list->last = list->first;
list->last->next = list->first;
list->first->before = list->last; size_t sz = list->size; Node* tmp;
while(sz-- > 0){
//p的next会被改变,所以提前保存
tmp = p->next;
while(t->next != list->first && p->data > t->next->data){
t = t->next;
}
//t为first,或者t为last,都是尾插
if(t->next == list->first){
t->next = p;
p->next = list->first;
p->before = t;
list->first->before = p;
list->last = p;
}
else{
p->next = t->next;
t->next->before = p; t->next = p;
p->before = t;
} p = tmp;
t = list->first;
}
} void resver(NodeList* list){
if(list->size == 1 || list->size == 0)return; //第一个节点
Node* head = list->first->next;
//第二个节点
Node* second = head->next;
//head就是last,所以要head->next = NULL;
list->last = head;
list->last->next = list->first;
list->first->before = list->last; Node* tmp;
while(second != list->first){
//必须保存second的next,因为下面的代码,会改变second的next
tmp = second->next; //头插
second->next = list->first->next;
list->first->next->before = second;
list->first->next = second;
second->before = list->first; second = tmp;
}
} void clear(NodeList* list){
Node* p = list->first->next;
while(p != list->last){
p = p->next;
free(p);
}
list->last = list->first;
list->last->next = list->first;
list->first->before = list->last;
list->size = 0;
} void destroy(NodeList* list){
clear(list);
free(list->first);
}

whileshuangnodemain.c

#include "whileshuangnode.h"

int main(){
NodeList list;
init(&list);
int select = 1;
ElemType item;
Node* node = NULL;
while(select){
printf("*****************************************\n");
printf("*** [1] push_back [2] push_front ***\n");
printf("*** [3] show_list [4] pop_back ***\n");
printf("*** [5] pop_front [6] insert_val ***\n");
printf("*** [7] find [8] length ***\n");
printf("*** [9] delete_val [10] sort ***\n");
printf("*** [11] sort [12] resver ***\n");
printf("*** [13] [14] clear ***\n");
printf("*** [0] quit [15*]destroy ***\n");
printf("*****************************************\n");
printf("请选择:>");
scanf("%d", &select);
if(0 == select)
break;
switch(select){
case 1:
printf("请输入要插入的数据,以-1结束>\n");
while(scanf("%d",&item) && item != -1){
push_back(&list, item);
}
show_list(&list);
break;
case 2:
printf("请输入要插入的数据,以-1结束>\n");
while(scanf("%d", &item) && item != -1){
push_front(&list, item);
}
show_list(&list);
break;
case 3:
show_list(&list);
break;
case 4:
pop_back(&list);
show_list(&list);
break;
case 5:
pop_front(&list);
show_list(&list);
break;
case 6:
printf("请输入要插入的数据>\n");
scanf("%d",&item);
insert_val(&list, item);
show_list(&list);
break;
case 7:
printf("please enter what you shoule find out>\n");
scanf("%d",&item);
node = find(&list, item);
if(node == NULL){
printf("can not find %d\n", item);
}
break;
case 8:
printf("length is %ld\n", list.size);
break;
case 9:
printf("please enter what you want to delete>\n");
scanf("%d",&item);
delete_val(&list, item);
show_list(&list);
break;
case 10:
// sort(&list);
//show_list(&list);
break;
case 11:
sort(&list);
show_list(&list);
break;
case 12:
resver(&list);
show_list(&list);
break;
case 13:
resver(&list);
show_list(&list);
break;
case 14:
clear(&list);
show_list(&list);
break;
case 15:
destroy(&list);
break;
default:
break;
}
} //destroy(&list);
}

c/c++ 线性表之双向循环链表的更多相关文章

  1. "《算法导论》之‘线性表’":双向循环链表

    本文双链表介绍部分参考自博文数组.单链表和双链表介绍 以及 双向链表的C/C++/Java实现. 1 双链表介绍 双向链表(双链表)是链表的一种.和单链表一样,双链表也是由节点组成,它的每个数据结点中 ...

  2. c/c++ 线性表之单向循环链表

    c/c++ 线性表之单向循环链表 线性表之单向循环链表 不是存放在连续的内存空间,链表中的每个节点的next都指向下一个节点,最后一个节点的下一个节点不是NULL,而是头节点.因为头尾相连,所以叫单向 ...

  3. 线性表->链式存储->双向链表

    文字描述 之前的链表(单链表.循环链表)的链式存储结构中只有一个指示直接后继的指针域.由此,从某个结点出发只能顺指针往后寻查其他结点.若要寻查结点的直接前驱,则需从表头指针出发.即单链表中,NextE ...

  4. 线性表->链式存储->循环链表

    文字描述 循环链表是另一种形式的链式存储结构.它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环.由此,从表中任一结点出发均可找到表中其他结点. 示意图 算法分析 插入.删除.查找等同单 ...

  5. C语言 线性表 双向链式结构 实现

    一个双向链式结构实现的线性表 duList (GCC编译). /** * @brief 线性表双向链表结构 * @author wid * @date 2013-10-28 * * @note 若代码 ...

  6. 数据结构(java版)学习笔记(四)——线性表之循环链表

    单向循环链表 PS:有阴影的结点是头结点 概念: 最后一个结点的链域值不为NULL,而是指向头结点 特点: 从表中的任意结点出发,都可以找到表中其他结点 循环条件 p==h 双向链表 概念 链表中的每 ...

  7. 线性表 (单链表、循环链表-python实现)

    一.线性表 线性表的定义: 线性表是具有相同数据类型的有限数据的序列. 线性表的特点: 出了第一个元素外,每个元素有且仅有一个直接前驱,除最后一个元素外有且只有一个后继. 线性表是一种逻辑结构,表示元 ...

  8. 线性表源码分享(c++),包含顺序表、单链表、循环链表、双向链表

    ---恢复内容开始--- 我是一个c++和数据结构的初学者,本文主要是把清华大学出版社的数据结构(用面向对象方法与c++语言描述)(第2版)这本书中第二章线性表的源码抄下来,在学习的过程中有助于加深印 ...

  9. C语言通用双向循环链表操作函数集

    说明 相比Linux内核链表宿主结构可有多个链表结构的优点,本函数集侧重封装性和易用性,而灵活性和效率有所降低.     可基于该函数集方便地构造栈或队列集.     本函数集暂未考虑并发保护. 一  ...

随机推荐

  1. 详谈js防抖和节流

    本文由小芭乐发表 0. 引入 首先举一个例子: 模拟在输入框输入后做ajax查询请求,没有加入防抖和节流的效果,这里附上完整可执行代码: <!DOCTYPE html> <html ...

  2. for循环中变量的作用域问题

    看下面这一行代码 for (let i = 0; i < 3; i++) { let i = 'abc' console.log(i) } // abc // abc // abc 从上面可以看 ...

  3. 使用MySQL组复制的限制和局限性

    本节列出和解释了组复制相关的要求和限制. 1.组复制的要求 要使用组复制,每个MySQL节点必须满足以下条件: 1.1 基本要求 InnoDB存储引擎:数据必须存储在事务型的InnoDB存储引擎中.事 ...

  4. iptables实战案例详解-技术流ken

    简介 关于iptables的介绍网上有很多的资料,大家可以自己找一些关于iptables的工作原理,以及四表五链的简介,对于学习iptables将会事半功倍.本博文将会例举几个工作中常用的iptabl ...

  5. spring-cloud-starter-gateway

    ********************************************************** Spring MVC found on classpath, which is i ...

  6. spark之JDBC开发(连接数据库测试)

    spark之JDBC开发(连接数据库测试) 以下操作属于本地模式操作: 1.在Eclipse4.5中建立工程RDDToJDBC,并创建一个文件夹lib用于放置第三方驱动包 [hadoop@CloudD ...

  7. RNN入门(4)利用LSTM实现整数加法运算

      本文将介绍LSTM模型在实现整数加法方面的应用.   我们以0-255之间的整数加法为例,生成的结果在0到510之间.为了能利用深度学习模型模拟整数的加法运算,我们需要将输入的两个加数和输出的结果 ...

  8. mssqlserver on linux - Linux下尝鲜MSSQL-SERVER【微软大法棒棒哒】

    微软的开源精神真是无敌了,接下来体验下Linux安装与使用MSSQL-SERVER! 安装说明 目前支持的平台: Red Hat Enterprise Linux 7.2 Get RHEL 7.2 U ...

  9. sudo: cd: command not found

    事件起因 今天在aws ubutun上忽然发现的一个问题,执行sudo cd 时出现 sudo: cd: command not found 原因 shell shell是一个命令解析器 所谓shel ...

  10. JSTL_Core标记库

    一.   说明 如有转载,请标明出处 本博讲解JSTL中的core库 对标记属性进行介绍时,首先介绍必写的属性,然后带有默认值的属性,其次是其余属性,这三类属性中间用空行隔开 二:core标记库库 C ...