双向链表
双向链表的特点
单向链表只有向后指的指针,如果向要获取前一个节点,需要从头遍历(单链表)或者通过当前位置循环遍历一圈(单向循环链表)。这样的话时间复杂度就会比较高。所以就有人提出一个节点包含两个指针,一个指针指向前一个节点,一个指针指向后一个节点,这种链表就是双向链表。
双向链表的结构
双向链表的节点格式
next(rear):指向后一个节点
pre(front):指向前一个节点
data:就是它的数据域
双向链表的结构体定义
#define datatype int
typedef struct node{
datatype data;
struct node *pre,*next;
}DPlist_t;
双向链表的结构
双向链表的操作
双向链表的创建
DPlist_t *DPListCreate(void)
{
DPlist_t *h;
h = (DPlist_t *)malloc(sizeof(*h));
if(h == NULL){
printf("malloc head node memory error\n");
return NULL;
}
h->data = (datatype)0;
h->pre = NULL;
h->next = NULL;
return h;
}
双向链表头插法
情况1:链表为空
情况2:链表不为空
双向链表节点插入(两上两下,从后往前操作)
int DPListInsertHead(DPlist_t* h, datatype data)
{
DPlist_t* tmp;
// 1.分配tmp节点,将data存放进去
tmp = (DPlist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("mallocnode memory error\n");
return -1;
}
tmp->data = data;
// 2.将tmp节点进行头插
tmp->next = h->next;
tmp->pre = h;
if(h->next != NULL)
h->next->pre = tmp;
h->next = tmp;
return 0;
}
双向链表遍历
void DPListShow(DPlist_t* h)
{
while (h->next) {
printf("-%d", h->next->data);
h = h->next;
}
printf("-\n");
while (h && h->pre) {
printf("-%d", h->data);
h = h->pre;
}
printf("-\n");
}
双向链表的尾插法
int DPListInsertTail(DPlist_t* h, datatype data)
{
DPlist_t* tmp;
// 1.分配tmp节点,将data存放进去
tmp = (DPlist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("mallocnode memory error\n");
return -1;
}
tmp->data = data;
// 2.让h走到双向链表的尾节点
while (h->next) {
h = h->next;
}
// 3.尾插法
tmp->next = h->next;
tmp->pre = h;
h->next = tmp;
return 0;
}
双向链表的位置插入
int DPListInsertPos(DPlist_t* h, int pos, datatype data)
{
DPlist_t* tmp;
// 1.判断位置是否合法
if (pos < 0) {
printf("插入的位置不正确\n");
return -1;
}
// 2通过循环找到插入的位置
while (h) {
if (pos > 0) {
pos--;
h = h->next;
} else {
// 3.分配tmp节点,将data存放进去
tmp = (DPlist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("mallocnode memory error\n");
return -1;
}
tmp->data = data;
// 4.将tmp节点插入
tmp->next = h->next;
tmp->pre = h;
if (h->next != NULL)
h->next->pre = tmp;
h->next = tmp;
return 0;
}
}
printf("插入的位置不正确\n");
return -1;
}
双链表的头删法
情况1:只有一个节点
情况2:有多个节点
操作流程:
步骤1:先判断双向链表是否是空
if(DPlistIsEmpty(h)){
return -1;
}
步骤2:让tmp记录h的下一个节点位置
tmp = h->next
步骤3:判断tmp的next是否是空
if(tmp->next != NULL){
tmp->next->pre = h;
}
步骤4:让h的next指向tmp的next
h->next = tmp->next;
步骤5:释放tmp
free(tmp);
tmp=NULL;
int DPListIsEmpty(DPlist_t* h)
{
return h->next == NULL ? 1 : 0;
}
datatype DPListDeleteHead(DPlist_t* h)
{
DPlist_t* tmp;
datatype data;
// 1.判断链表是否是空
if (DPListIsEmpty(h)) {
printf("链表是空,头删失败\n");
return (datatype)-1;
}
// 2.让tmp记录h的下一个节点位置
tmp = h->next;
// 3.判断tmp的next是否是空
if (tmp->next != NULL) {
tmp->next->pre = h;
}
// 4.让h的next指向tmp的next
h->next = tmp->next;
//5.释放tmp
data = tmp->data;
if(tmp != NULL){
free(tmp);
tmp = NULL;
}
return data;
}
位置删除
情况1:只有一个节点
情况2:有多个节点
删除的思路:
步骤1:判断双向链表是否是空,如果是空,退出
if(DPlistIsEmpty(h)){
return -1;
}
步骤2通过循环找到删除的位置
while(h->next){
if(pos>0){
pos--;
h = h->next;
}else{
//已经找到位置了
}
}
步骤3:让tmp指向h->next位置
tmp= h->next;
步骤4:开始删除
if(tmp->next != NULL){
tmp->next->pre = h;
}
h->next = tmp->next;
free(tmp);
tmp=NULL;
datatype DPListDeletePos(DPlist_t* h, int pos)
{
DPlist_t* tmp;
datatype data;
// 1.判断pos是否合法
if (pos < 0) {
printf("位置不合法,无法删除\n");
return (datatype)-1;
}
// 2.判断链表是否是空
if (DPListIsEmpty(h)) {
printf("双向链表是空,删除失败\n");
return (datatype)-1;
}
// 3.通过循环找到删除的位置
while (h->next) {
if (pos > 0) {
pos--;
h = h->next;
} else {
// 4.让tmp指向h的后一个节点
tmp = h->next;
// 5.判断tmp是否是最后一个节点
if (tmp->next != NULL) {
tmp->next->pre = h;
}
// 6.让h的next指针指向tmp的next
h->next = tmp->next;
// 7.释放tmp内存
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
}
printf("位置不合法,无法删除\n");
return (datatype)-1;
}
位置查询
datatype DPListCheckDataByPos(DPlist_t* h, int pos)
{
datatype data;
// 1.判断pos是否合法
if (pos < 0) {
printf("位置不合法,无法查询\n");
return (datatype)-1;
}
// 2.通过循环找到查询的位置
while (h->next) {
if (pos > 0) {
pos--;
h = h->next;
} else {
data = h->next->data;
return data;
}
}
printf("位置不合法,无法查询\n");
return (datatype)-1;
}
位置更新
int DPListUpdateDataByPos(DPlist_t *h,int pos,datatype data)
{
// 1.判断pos是否合法
if (pos < 0) {
printf("位置不合法,无法更新\n");
return -1;
}
// 2.通过循环找到更新的位置
while (h->next) {
if (pos > 0) {
pos--;
h = h->next;
} else {
h->next->data = data;
return 0;
}
}
printf("位置不合法,无法更新\n");
return -1;
}
双向链表整体代码
DPlist.h
#ifndef __DPLIST_H__
#define __DPLIST_H__
#include <stdio.h>
#include <stdlib.h>
#define datatype int
typedef struct node{
datatype data;
struct node *pre,*next;
}DPlist_t;
DPlist_t *DPListCreate(void);
int DPListInsertHead(DPlist_t *h,datatype data);
int DPListInsertTail(DPlist_t *h,datatype data);
int DPListInsertPos(DPlist_t *h,int pos,datatype data);
void DPListShow(DPlist_t *h);
datatype DPListDeleteHead(DPlist_t *h);
datatype DPListDeletePos(DPlist_t *h,int pos);
datatype DPListCheckDataByPos(DPlist_t *h,int pos);
int DPListUpdateDataByPos(DPlist_t *h,int pos,datatype data);
#endif
DPlist.c
#include "DPlist.h"
DPlist_t* DPListCreate(void)
{
DPlist_t* h;
h = (DPlist_t*)malloc(sizeof(*h));
if (h == NULL) {
printf("malloc head node memory error\n");
return NULL;
}
h->data = (datatype)0;
h->pre = NULL;
h->next = NULL;
return h;
}
int DPListInsertHead(DPlist_t* h, datatype data)
{
DPlist_t* tmp;
// 1.分配tmp节点,将data存放进去
tmp = (DPlist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("mallocnode memory error\n");
return -1;
}
tmp->data = data;
// 2.将tmp节点进行头插
tmp->next = h->next;
tmp->pre = h;
if (h->next != NULL)
h->next->pre = tmp;
h->next = tmp;
return 0;
}
void DPListShow(DPlist_t* h)
{
while (h->next) {
printf("-%d", h->next->data);
h = h->next;
}
printf("-\n");
while (h && h->pre) {
printf("-%d", h->data);
h = h->pre;
}
printf("-\n");
puts("----------------------------------------");
}
int DPListInsertTail(DPlist_t* h, datatype data)
{
DPlist_t* tmp;
// 1.分配tmp节点,将data存放进去
tmp = (DPlist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("mallocnode memory error\n");
return -1;
}
tmp->data = data;
// 2.让h走到双向链表的尾节点
while (h->next) {
h = h->next;
}
// 3.尾插法
tmp->next = h->next;
tmp->pre = h;
h->next = tmp;
return 0;
}
int DPListInsertPos(DPlist_t* h, int pos, datatype data)
{
DPlist_t* tmp;
// 1.判断位置是否合法
if (pos < 0) {
printf("插入的位置不正确\n");
return -1;
}
// 2通过循环找到插入的位置
while (h) {
if (pos > 0) {
pos--;
h = h->next;
} else {
// 3.分配tmp节点,将data存放进去
tmp = (DPlist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("mallocnode memory error\n");
return -1;
}
tmp->data = data;
// 4.将tmp节点插入
tmp->next = h->next;
tmp->pre = h;
if (h->next != NULL)
h->next->pre = tmp;
h->next = tmp;
return 0;
}
}
printf("插入的位置不正确\n");
return -1;
}
int DPListIsEmpty(DPlist_t* h)
{
return h->next == NULL ? 1 : 0;
}
datatype DPListDeleteHead(DPlist_t* h)
{
DPlist_t* tmp;
datatype data;
// 1.判断链表是否是空
if (DPListIsEmpty(h)) {
printf("链表是空,头删失败\n");
return (datatype)-1;
}
// 2.让tmp记录h的下一个节点位置
tmp = h->next;
// 3.判断tmp的next是否是空
if (tmp->next != NULL) {
tmp->next->pre = h;
}
// 4.让h的next指向tmp的next
h->next = tmp->next;
// 5.释放tmp
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
datatype DPListDeletePos(DPlist_t* h, int pos)
{
DPlist_t* tmp;
datatype data;
// 1.判断pos是否合法
if (pos < 0) {
printf("位置不合法,无法删除\n");
return (datatype)-1;
}
// 2.判断链表是否是空
if (DPListIsEmpty(h)) {
printf("双向链表是空,删除失败\n");
return (datatype)-1;
}
// 3.通过循环找到删除的位置
while (h->next) {
if (pos > 0) {
pos--;
h = h->next;
} else {
// 4.让tmp指向h的后一个节点
tmp = h->next;
// 5.判断tmp是否是最后一个节点
if (tmp->next != NULL) {
tmp->next->pre = h;
}
// 6.让h的next指针指向tmp的next
h->next = tmp->next;
// 7.释放tmp内存
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
}
printf("位置不合法,无法删除\n");
return (datatype)-1;
}
datatype DPListCheckDataByPos(DPlist_t* h, int pos)
{
datatype data;
// 1.判断pos是否合法
if (pos < 0) {
printf("位置不合法,无法查询\n");
return (datatype)-1;
}
// 2.通过循环找到查询的位置
while (h->next) {
if (pos > 0) {
pos--;
h = h->next;
} else {
data = h->next->data;
return data;
}
}
printf("位置不合法,无法查询\n");
return (datatype)-1;
}
int DPListUpdateDataByPos(DPlist_t *h,int pos,datatype data)
{
// 1.判断pos是否合法
if (pos < 0) {
printf("位置不合法,无法更新\n");
return -1;
}
// 2.通过循环找到查询的位置
while (h->next) {
if (pos > 0) {
pos--;
h = h->next;
} else {
h->next->data = data;
return 0;
}
}
printf("位置不合法,无法更新\n");
return -1;
}
main.c
#include "DPlist.h"
int main(int argc, const char* argv[])
{
DPlist_t* h;
h = DPListCreate();
if (h == NULL) {
printf("DP list create error\n");
return -1;
}
DPListInsertHead(h, 40);
DPListInsertHead(h, 30);
DPListInsertHead(h, 20);
DPListInsertHead(h, 10);
DPListShow(h);
DPListInsertTail(h, 50);
DPListShow(h);
DPListInsertPos(h, 0, 222);
DPListShow(h);
// printf("delete head = %d\n",DPListDeleteHead(h));
// printf("delete head = %d\n",DPListDeleteHead(h));
// printf("delete head = %d\n",DPListDeleteHead(h));
// printf("delete head = %d\n",DPListDeleteHead(h));
// DPListShow(h);
// printf("delete pos = %d\n", DPListDeletePos(h, 0));
// printf("delete pos = %d\n", DPListDeletePos(h, 4));
// printf("delete pos = %d\n", DPListDeletePos(h, 0));
// printf("delete pos = %d\n", DPListDeletePos(h, 0));
// printf("delete pos = %d\n", DPListDeletePos(h, 0));
// printf("delete pos = %d\n", DPListDeletePos(h, 0));
// printf("delete pos = %d\n", DPListDeletePos(h, 0));
// DPListShow(h);
printf("Check pos = %d\n", DPListCheckDataByPos(h, 3));
DPListUpdateDataByPos(h,3,7777);
printf("Check pos = %d\n", DPListCheckDataByPos(h, 3));
DPListShow(h);
return 0;
}
双向循环链表
双向循环链表的特点
双向循环链表和双向链表的结构都是一样的,只不过双向循环链表的尾结点不指向NULL,而是指向了头节点。
双向循环链表的头节点也不指向NULL,而是指向尾节点。
双向循环链表的结构
#define datatype int
typedef struct node{
datatype data;
struct node *pre,*next;
}DPloop_t;
双向循环链表的操作
双向循环链表的创建
DPloop_t* DPLoopCreate(void)
{
DPloop_t* h;
h = (DPloop_t*)malloc(sizeof(*h));
if (h == NULL) {
printf("malloc DP head node error\n");
return NULL;
}
h->data = (datatype)0;
h->next = h;
h->pre = h;
return h;
}
双向循环链表的头插法
int DPLoopInsertHead(DPloop_t* h, datatype data)
{
DPloop_t* tmp;
// 1.分配节点的内存,并将data存放进去
tmp = (DPloop_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("malloc node memory error\n");
return -1;
}
tmp->data = data;
// 2.进行头插法
tmp->next = h->next;
tmp->pre = h;
h->next->pre = tmp;
h->next = tmp;
return 0;
}
双向循环链表的头删法
头删的思想:
步骤1:双向循环链表的判空
if(DPLoopIsEmpty(h)){
return -1;
}
步骤2:让tmp指向h的下一个节点
tmp = h->next;
步骤3:让tmp的下一个节点的pre指向h
tmp->next->pre = h;
步骤4:让h的next指向tmp的下一个节点
h->next = tmp->next
步骤5:释放tmp
free(tmp);
tmp = NULL;
datatype DPLoopDeleteHead(DPloop_t* h)
{
datatype data;
DPloop_t* tmp;
// 1.判空
if (DPLoopIsEmpty(h)) {
printf("双向循环链表是空,头删失败\n");
return (datatype)-1;
}
// 2.让tmp指向h的下一个节点
tmp = h->next;
// 3.让tmp的下一个节点的pre指向h
tmp->next->pre = h;
// 4.让h的next指向tmp的下一个节点
h->next = tmp->next;
// 5.释放tmp
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
双向循环链表的遍历
void DPLoopShow(DPloop_t* h)
{
DPloop_t* th = h;
while (h->next != th) {
printf("-%d", h->next->data);
h = h->next;
}
printf("-\n");
while (h->pre != th) {
printf("-%d", h->data);
h = h->pre;
}
printf("-%d-\n", h->data);
}
双向循环链表的整体代码
DPloop.h
#ifndef __DPLOOP_H__
#define __DPLOOP_H__
#include <stdio.h>
#include <stdlib.h>
#define datatype int
typedef struct node{
datatype data;
struct node *pre,*next;
}DPloop_t;
DPloop_t * DPLoopCreate(void);
int DPLoopInsertHead(DPloop_t *h,datatype data);
datatype DPLoopDeleteHead(DPloop_t *h);
void DPLoopShow(DPloop_t *h);
#endif
DPloop.c
#include "DPloop.h"
DPloop_t* DPLoopCreate(void)
{
DPloop_t* h;
h = (DPloop_t*)malloc(sizeof(*h));
if (h == NULL) {
printf("malloc DP head node error\n");
return NULL;
}
h->data = (datatype)0;
h->next = h;
h->pre = h;
return h;
}
int DPLoopInsertHead(DPloop_t* h, datatype data)
{
DPloop_t* tmp;
// 1.分配节点的内存,并将data存放进去
tmp = (DPloop_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("malloc node memory error\n");
return -1;
}
tmp->data = data;
// 2.进行头插法
tmp->next = h->next;
tmp->pre = h;
h->next->pre = tmp;
h->next = tmp;
return 0;
}
int DPLoopIsEmpty(DPloop_t* h)
{
return h->next == h ? 1 : 0;
}
datatype DPLoopDeleteHead(DPloop_t* h)
{
datatype data;
DPloop_t* tmp;
// 1.判空
if (DPLoopIsEmpty(h)) {
printf("双向循环链表是空,头删失败\n");
return (datatype)-1;
}
// 2.让tmp指向h的下一个节点
tmp = h->next;
// 3.让tmp的下一个节点的pre指向h
tmp->next->pre = h;
// 4.让h的next指向tmp的下一个节点
h->next = tmp->next;
// 5.释放tmp
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
void DPLoopShow(DPloop_t* h)
{
DPloop_t* th = h;
while (h->next != th) {
printf("-%d", h->next->data);
h = h->next;
}
printf("-\n");
while (h->pre != th) {
printf("-%d", h->data);
h = h->pre;
}
printf("-%d-\n", h->data);
}
main.c
#include "DPloop.h"
int main(int argc, char const* argv[])
{
DPloop_t* h;
h = DPLoopCreate();
if (h == NULL) {
printf("create error\n");
return -1;
}
DPLoopInsertHead(h, 40);
DPLoopInsertHead(h, 30);
DPLoopInsertHead(h, 20);
DPLoopInsertHead(h, 10);
DPLoopShow(h);
printf("delete head = %d\n", DPLoopDeleteHead(h));
printf("delete head = %d\n", DPLoopDeleteHead(h));
printf("delete head = %d\n", DPLoopDeleteHead(h));
printf("delete head = %d\n", DPLoopDeleteHead(h));
printf("delete head = %d\n", DPLoopDeleteHead(h));
DPLoopShow(h);
return 0;
}