數據結構(6)線性表之雙循環鏈表
前言
同單鏈表一樣,雙向鏈表也可以是循環表。鏈表最後一個結點的next指針指向頭結點,頭結點的Prior指針指向最後一個結點,形成循環。
帶頭結點的空鏈表
帶頭結點的非空鏈表
添加管理結構
雙循環鏈表的插入與刪除
實際上,在循環鏈表中,無論是頭部插入還是尾部插入,都可以理解爲按位置插入(就是在兩個結點中間進行插入):頭部插入是在頭結點和首元結點之間進行插入,尾部插入則是在最後一個結點和頭結點之間進行插入。這樣,我們只需要知道如何在兩個結點中間進行插入,就可以實現雙循環鏈表諸多的插入操作。而如何在兩個結點中間進行插入,跟在雙鏈表中是一致的,也就是需要考慮四個指針的指向
-
待插入結點的Prior指針
-
待插入結點的Next指針
-
待插入結點前驅的Next指針
-
待插入結點後繼的Prior指針
這樣,本來區分開的頭部插入、尾部插入操作可以整合爲按位置插入,參數傳要插入的位置即可。但是考慮到我們的雙鏈表增加了管理結構,還需要保證管理結構指針的正確性,所以在代碼中仍是區分實現的。
尾部插入也是兩個結點中的插入
實際上
插入與刪除類似
其他操作同雙鏈表一致,只是判斷循環結束的條件有區別罷了,不細說
全部代碼
DCList.h
#ifndef DCList_h
#define DCList_h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define ElemType int
typedef struct Node{
ElemType data;
struct Node *prior;
struct Node *next;
}Node,*PNode;
typedef struct List{
PNode first;
PNode last;
int size;
}List;
//初始化
void InitDCList(List *list);
//1.尾部插入
void push_back(List *list,ElemType x);
//2.頭部插入
void push_fount(List *list,ElemType x);
//3.展示
void show_list(List *list);
//4.尾部刪除
void pop_back(List *list);
//5.頭部刪除
void pop_fount(List *list);
//6.按值插入(要求插入前的鏈表是有序的(此處爲順序
void insert_val(List *list,ElemType x);
//7.按值查找
Node* find(List *list,ElemType x);
//8.獲取長度
int length(List *list);
//9.按值刪除
void delete_val(List *list,ElemType x);
//10.排序
void sort(List *list);
//11.逆置(前後轉換
void resver(List *list);
//12.清除單鏈表 ->只清除數據,保留頭結點
void clearList(List *list);
//13.摧毀 ->包括頭結點在內一起摧毀
void destroy(List *list);
//生成一個結點
Node* getNode(ElemType x);
#endif /* DCList_h */
DCList.cpp
#include "DCList.h"
//初始化
void InitDCList(List *list){
//申請頭結點
Node *s = (Node *)malloc(sizeof(Node));
assert(s != NULL);
list->first = list->last = s;
//使鏈表循環
list->first->prior = list->last;
list->last->next = list->first;
list->size = 0;
}
//1.尾部插入
void push_back(List *list,ElemType x){
Node *s = getNode(x);
//s的前驅指向最後一個結點
s->prior = list->last;
//s的後繼指向頭結點
s->next = list->last->next;
//頭結點的前驅指向s
//list->last->next->prior = s;
s->next->prior = s;
//最後一個結點的後繼指向s
list->last->next = s;
//重新設置last指針的值
list->last = s;
list->size ++;
}
//2.頭部插入
void push_fount(List *list,ElemType x){
Node *s = getNode(x);
//s的後繼是頭結點的後繼
s->next = list->first->next;
//s的前驅是頭結點
s->prior = list->first;
//原先第一個結點的前驅改爲s
//list->first->next->prior = s;
s->next->prior = s;
//頭結點的後繼改爲s
list->first->next = s;
//判斷是否是第一個結點
if (list->first == list->last) {
//是,需要修改last指針的指向
list->last = s;
}
list->size ++;
}
//3.展示
void show_list(List *list){
Node *s = list->first->next;
while (s != list->first) {
printf("%4d",s->data);
s = s->next;
}
printf("\n");
}
//4.尾部刪除
void pop_back(List *list){
if (list->size == 0) {
printf("鏈表已空!\n");
return;
}
Node *s = list->last;
//last指針修改爲s的前驅
//list->last = list->last->prior;
list->last = s->prior;
//s前驅的next指針指向s的後繼(即頭結點)
//list->last->next = s->next;
s->prior->next = s->next;
//s後繼的prior指針(即頭結點的prior指針)指向s的前驅(即新的最後一個結點)
//s->next->prior = list->last;
s->next->prior = s->prior;
//釋放s
free(s);
list->size --;
}
//5.頭部刪除
void pop_fount(List *list){
if (list->size == 0) {
printf("鏈表已空!\n");
return;
}
Node *s = list->first->next;
//s前驅的後繼指向s的後繼(即頭結點的next指向s的next)
//list->first->next = s->next;
s->prior->next = s->next;
//s後繼的前驅指向s的前驅(即s的後繼的prior指針指向頭結點)
//s->next->prior = list->first;
s->next->prior = s->prior;
//釋放s
free(s);
//判斷刪除的是否是最後一個結點
if (list->size == 1) {
//是,重新修改last指針的指向
list->last = list->first;
}
list->size -- ;
}
//6.按值插入(要求插入前的鏈表是有序的(此處爲順序
void insert_val(List *list,ElemType x){
Node *p = list->first;
//尋找插入位置
while (p->next != list->first && p->next->data < x) {
p = p->next;
}
//在p結點之後進行插入
//判斷插入位置
if (p->next == list->first) {
//尾部插入
push_back(list, x);
}else{
Node *s = getNode(x);
//s的前驅爲p
s->prior = p;
//s的後繼爲p的後繼
s->next = p->next;
//p後繼的前驅改爲s
p->next->prior = s;
//p的後繼改爲s
p->next = s;
list->size ++;
}
}
//7.按值查找
Node* find(List *list,ElemType x){
Node *s = list->first->next;
while (s != list->first && s->data != x) {
s = s->next;
}
if (s == list->first) {
//循環結束,沒找到
return NULL;
}
return s;
}
//8.獲取長度
int length(List *list){
return list->size;
}
//9.按值刪除
void delete_val(List *list,ElemType x){
if (list->size == 0) {
printf("鏈表已空!\n");
return;
}
Node *p = find(list, x);
if (p == NULL) {
printf("要刪除的數據不存在!\n");
return;
}
//找到結點位置
if (p == list->last) {
//如果是最後一個,直接進行尾部刪除
push_back(list, x);
}else{
//p後繼的前驅指向p的前驅
p->next->prior = p->prior;
//p前驅的後繼指向p的後繼
p->prior->next = p->next;
list->size --;
}
}
//10.排序
void sort(List *list){
if (list->size <= 1) {
printf("無須排序!\n");
return;
}
//將鏈表分爲兩個部分,s單獨作爲一個鏈表,p指向後續結點
Node *s = list->first->next;
Node *p = s->next;
//將後續結點的最後一個結點的next指針標記爲空
list->last->next = NULL;
//重新設置last指針的值
list->last = s;
//讓鏈表循環
//s的後繼指向頭結點
//list->last->next = list->first;
s->next = list->first;
//頭結點的前驅指向s
//list->first->prior = list->last;
list->first->prior = s;
while (p != NULL) {
s = p;
p = p->next;
//直接進行按值插入
insert_val(list, s->data);
free(s);
list->size --;
}
}
//11.逆置(前後轉換
void resver(List *list){
if (list->size <= 1) {
printf("無須逆置!\n");
return;
}
//將鏈表分爲兩個部分,s單獨作爲一個鏈表,p指向後續結點
Node *s = list->first->next;
Node *p = s->next;
//將後續結點的最後一個結點的next指針標記爲空
list->last->next = NULL;
//重新設置last指針的值
list->last = s;
//讓鏈表循環
//s的後繼指向頭結點
//list->last->next = list->first;
s->next = list->first;
//頭結點的前驅指向s
//list->first->prior = list->last;
list->first->prior = s;
while (p != NULL) {
s = p;
p = p->next;
//直接進行頭部插入
push_fount(list, s->data);
free(s);
list->size --;
}
}
//12.清除單鏈表 ->只清除數據,保留頭結點
void clearList(List *list){
if (list->size == 0) {
return;
}
Node *s = list->first;
Node *p = s->next;
//遍歷整個鏈表,釋放結點
while (p != list->first) {
s = p;
p = p->next;
free(s);
list->size --;
}
//重新設置last指針
list->last = list->first;
//使鏈表循環
list->first->prior = list->last;
list->last->next = list->first;
}
//13.摧毀 ->包括頭結點在內一起摧毀
void destroy(List *list){
clearList(list);
free(list->first);
list->first = list->last = NULL;
}
//生成一個結點
Node* getNode(ElemType x){
//申請空間
Node *s = (Node *)malloc(sizeof(Node));
assert(s != NULL);
//賦初值
s->data = x;
s->prior = s->next = NULL;
//返回
return s;
}
Main.cpp
#include "DCList.h"
int main(int argc, const char * argv[]) {
List myList;
InitDCList(&myList);
//保存要輸入的數據
ElemType item;
//保存要查找的地址
Node *p = NULL;
int select = 1;
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] resver [12] clear *\n");
printf("* [13*] destroy [0] quit_system*\n");
printf("**************************************\n");
printf("請選擇:");
scanf("%d",&select);
if (select == 0) {
break;
}
switch (select) {
case 1:
//尾部插入
printf("請輸入要插入的數據(-1結束):");
scanf("%d",&item);
while (item != -1) {
push_back(&myList, item);
scanf("%d",&item);
}
break;
case 2:
//頭部插入
printf("請輸入要插入的數據(-1結束):");
scanf("%d",&item);
while (item != -1) {
push_fount(&myList, item);
scanf("%d",&item);
}
break;
case 3:
//展示單鏈表
show_list(&myList);
break;
case 4:
//尾部刪除
pop_back(&myList);
break;
case 5:
//頭部刪除
pop_fount(&myList);
break;
case 6:
//按值插入
printf("請輸入要插入的數據:\n");
scanf("%d",&item);
insert_val(&myList, item);
break;
case 7:
//按值查找
printf("請輸入要查找的值:\n");
scanf("%d",&item);
p = find(&myList, item);
if (p == NULL) {
printf("要查找的數據不存在!\n");
}else{
printf("地址爲:%p\n",p);
}
break;
case 8:
//展示鏈表長度
printf("鏈表的長度爲:%d\n",length(&myList));
break;
case 9:
//按值刪除
printf("請輸入要刪除的值:\n");
scanf("%d",&item);
delete_val(&myList, item);
break;
case 10:
//排序
sort(&myList);
break;
case 11:
//逆置(前後轉換
resver(&myList);
break;
case 12:
//清除
clearList(&myList);
break;
case 13:
destroy(&myList);
break;
default:
printf("輸入的命令有誤,請重新插入\n");
break;
}
}
return 0;
}