c/c++ 线性表之双向链表
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 | 释放所有节点的所占用的内存空间,包括头节点 |
shuangnode.h
#ifndef __SHUANGNODE__
#define __SHUANGNODE__
#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
shuangnode.c
#include "shuangnode.h"
void init(NodeList* list){
list->first = (Node*)malloc(sizeof(Node));
list->last = list->first;
list->last->next = NULL;
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 = NULL;
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;
p->next = list->first->next;
//第一次添加节点的时候要移动未指针
if(NULL == list->first->next){
list->last = p;
}
//不是第一次添加节点的时候,要把原第一个节点的before指向,新添加的节点
else{
list->first->next->before = p;
}
//设置头指针的next节点
list->first->next = p;
list->size++;
}
void show_list(NodeList* list){
Node* tmp = list->first->next;
while(tmp != NULL){
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 = NULL;
//让尾指针指向原尾节点的前一个节点
list->last = list->last->before;
list->size--;
}
void pop_front(NodeList* list){
if(list->size == 0)return;
free(list->first->next);
//就剩一个节点的时候,要移动尾指针。因为list->first->next已经为NULL,下面的list->first->next->before就会在执行时候崩掉,所以要return掉
if(list->first->next == list->last){
list->last = list->first;
list->last->next = NULL;
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 != NULL && val > p->next->data){
p = p->next;
}
//第一次加节点,或者,最后一个节点也没有给的值大的时候
if(NULL == p->next){
n->next = NULL;
n->before = list->last;
list->last->next = 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 != NULL && p->next->data != val){
p = p->next;
}
if(NULL == p->next){
return NULL;
}
printf("%d is found\n", p->next->data);
return p->next;
}
//寻找给定值的节点的前一个节点的位置
Node* find1(NodeList* list, ElemType val){
if(list->size == 0)return NULL;
Node* p = list->first;
while(p->next != NULL && p->next->data != val){
p = p->next;
}
if(NULL == p->next){
return NULL;
}
printf("%d is found\n", p->next->data);
return p;
}
void delete_val(NodeList* list, ElemType val){
Node* p = find1(list, val);
if(NULL == p) return;
free(p->next);
//删除的节点是尾节点的时候,要移动last
if(p->next == list->last){
list->last = p;
p->next = NULL;
list->size--;
return;
}
p->next->next->before = p;
p->next = p->next->next;
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 = NULL;
size_t sz = list->size;
Node* tmp;
while(sz-- > 0){
//p的next会被改变,所以提前保存
tmp = p->next;
while(t->next != NULL && p->data > t->next->data){
t = t->next;
}
//t为first,或者t为last,都是尾插
if(t->next == NULL){
t->next = p;
p->next = NULL;
p->before = t;
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 = NULL;
Node* tmp;
while(second != NULL){
//必须保存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 != NULL){
free(p);
p = p->next;
}
list->last = list->first;
list->last->next = NULL;
list->size = 0;
}
void destroy(NodeList* list){
Node* p = list->first;
while(p != NULL){
free(p);
p = p->next;
}
list->size = 0;
}
shuangnodemain.c
#include "shuangnode.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++ 线性表之双向链表的更多相关文章
- [数据结构 - 第3章] 线性表之双向链表(C语言实现)
一.什么是双向链表? 双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域.所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前 ...
- 【Java】 大话数据结构(5) 线性表之双向链表
本文根据<大话数据结构>一书,实现了Java版的双向链表. 在每个数据结点中都有两个指针,分别指向直接后继和直接前驱,这样的链表称为双向链表. 双向链表的结构如图所示: 查找元素可以根据元 ...
- (续)线性表之双向链表(C语言实现)
在前文实现单向链表的基本操作下,本文实现双向链表的基本操作. 双向链表与单链表差异,是双向链表结点中有前向指针和后向指针.所以在插入和删除新结点元素时候不见要考虑后向指针还要考虑前向指针. 以下是双向 ...
- 【algo&ds】2.线性表
1.线性表 线性表(英语:Linear List)是由n(n≥0)个数据元素(结点)a[0],a[1],a[2]-,a[n-1]组成的有限序列. 其中: 数据元素的个数n定义为表的长度 = " ...
- 玩转C线性表和单向链表之Linux双向链表优化
前言: 这次介绍基本数据结构的线性表和链表,并用C语言进行编写:建议最开始学数据结构时,用C语言:像栈和队列都可以用这两种数据结构来实现. 一.线性表基本介绍 1 概念: 线性表也就是关系户中最简单的 ...
- 线性表->链式存储->双向链表
文字描述 之前的链表(单链表.循环链表)的链式存储结构中只有一个指示直接后继的指针域.由此,从某个结点出发只能顺指针往后寻查其他结点.若要寻查结点的直接前驱,则需从表头指针出发.即单链表中,NextE ...
- 线性表的Java实现--链式存储(双向链表)
有了单向链表的基础,双向链表的实现就容易多了. 双向链表的一般情况: 增加节点: 删除节点: 双向链表的Java实现: package com.liuhao.algorithm; publi ...
- 线性表源码分享(c++),包含顺序表、单链表、循环链表、双向链表
---恢复内容开始--- 我是一个c++和数据结构的初学者,本文主要是把清华大学出版社的数据结构(用面向对象方法与c++语言描述)(第2版)这本书中第二章线性表的源码抄下来,在学习的过程中有助于加深印 ...
- 数据结构(Java描述)之线性表
基础概念 数据结构:是相互之间存在一种或多种关系的数据元素的集合. 逻辑结构和物理结构 关于数据结构,我们可以从逻辑结构和物理结构这两个维度去描述 逻辑结构是数据对象中数据元素之间的关系,是从逻辑意义 ...
随机推荐
- 痞子衡嵌入式:恩智浦LPC系列MCU开发那些事 - 索引
大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家介绍的是恩智浦LPC系列微控制器相关知识. 恩智浦半导体最早于2003年便开始推出LPC系列MCU,但早期的产品LPC2000/3000系列属 ...
- MySQL的binlog恢复(Windows下)
前言 在最近的工作中,由于自己粗(zuo)心(si)误update操作导致几百行的数据出现错误,在心急如焚的同时(那时候我竟然不知道除了备份之后还有binlog日志恢复)立马查资料学习binlog的恢 ...
- C#模拟httpwebrequest请求_向服务器模拟cookie发送
使用C#代码模拟web请求,是一种常用的方法,以前没专门整理过,这里暂时贴上自己整理的完整代码,以后再做梳理: public class MyRequest { #region 辅助方法 public ...
- js treeData 树形数据结构 无限层级(转载)
js实现无限层级树形数据结构(创新算法) 转载:https://blog.csdn.net/Mr_JavaScript/article/details/82817177 由于做项目的需要,把一个线性数 ...
- (8)Microsoft office Word 2013版本操作入门_制作传单海报
1.纸张大小,方向设定. 1.1纸张大小: [页面布局]----[纸张大小] 可以选择已有的尺寸,也可以选择其他自定义的大小. 1.2 方向设定: [页面布局]--[纸张方向]选择 横向或者纵向 2. ...
- VSCode Snippet 小试牛刀
这几天因为一个需求,要不断重复一个用特定代码段去包围不同代码的需求. 这个要不断移动鼠标以及重复敲打相同代码的体力活,实在让我老眼昏花,体内的懒人之力迫使我想一个快捷的方法来代替之. 之前就知道Sni ...
- Java并发编程-ReentrantReadWriteLock
基于AQS的前世今生,来学习并发工具类ReentrantReadWriteLock.本文将从ReentrantReadWriteLock的产生背景.源码原理解析和应用来学习这个并发工具类. 1. 产生 ...
- 数据库sql语句常见面试题
转载:本文转载自:https://blog.csdn.net/woshinidedege/article/details/78659202 一.有以下几张表及表结构Student(Sid,Sname, ...
- 【软工神话】第四篇(Alpha冲刺)
前言:咳咳,一个多月了,最近忙这忙那,都把这个故事给落下了,昨晚刚回到家,白天得干活,晚上赶紧把这个故事写完,以此纪念自己的软工. 说明:故事中的人物均是化名,故事情节经过些许加工,故事情节并没有针对 ...
- js 从一个对象中找到属性值相等的集合
getobjs: function(objs, key, value) { var result = []; for (var i in objs) { var obj = $(objs[i]); i ...