线性表的链式存储-双向链表,认识的反复性,实践认识再实践再认识

“单链表能够独立实现,那么双链表的实现也就干脆利落了”,这是我实现完单链表之后的想法。于是受惯性思维的驱使,大部分都是照搬单链表,甚至可以说是直接复制过来。
后来我仔细一想:单链表一个数据域,一个指针域(指向后继结点);双链表一个数据域,两个指针域,一个指向前驱结点,一个指向后继结点。双链表的功能是实现了,可其前指针域貌似没怎么使用。
于是我把双链表代码推倒重来,在实现双链表的过程中,尽可能多的使用前指针域。不这样做的话,那双链表跟单链表有什么区别?双链表的存在意义何在?

一、谈谈链表的定义及个人认识

前指针域:双向链表中指向前驱结点的指针

后指针域:双向链表中指向后继结点的指针

首结点:。。。
尾结点:。。。
起源结点:首结点之前的那个结点;或者说确定首结点的结点。起源结点的数据类型和首结点的数据类型相同,不存放有效数据,只是为了方便对链表的操作,其数据域可用来存放有效结点个数。

断链:解除两个结点之间的联系
牵链:让两个结点产生联系,通过其中一个结点确定另一个结点

首指针:指向首节点的指针变量
尾指针:指向尾节点的指针变量
起源指针:指向起源节点的指针变量

首领/头领,这两个词意思是相同的,斩首/砍头,这两个词也是一个意思,那么我的问题就来了,首和头是不是相同的意思?书中讲到的头节点/首元节点,我感觉很怪异,并没有去使用,而是另取名字。它们和首结点从词义上来讲,听起来有些冲突,所以我把所谓的头结点/首元结点称作起源节点。

确定一个双链表需要的参数:起源指针或者尾指针,我们通过这两个指针任意一个都可以推算出链表中的其他结点。

二、运行效果

1.初始化双链表

在这里插入图片描述
在这里插入图片描述

2.遍历双链表

在这里插入图片描述

3.追加结点

在这里插入图片描述

4.删除结点

输入有效下标删除结点

在这里插入图片描述

输入无效下标删除结点:

等于0的下标,无效
在这里插入图片描述
大于结点个数的下标,无效
在这里插入图片描述
小于0的下标,无效
在这里插入图片描述

5.插入结点

输入有效下标插入结点

在这里插入图片描述

输入无效下标插入结点:

下标等于0,无效
在这里插入图片描述
下标大于结点个数,无效
在这里插入图片描述
下标等于0,无效
在这里插入图片描述

6.修改结点

输入有效下标修改结点

在这里插入图片描述

输入无效下标修改结点:

输入下标等于0,无效
在这里插入图片描述
输入下标小于0,无效
在这里插入图片描述
输入下标大于结点个数,无效
在这里插入图片描述

7.按结点下标取值

输入有效下标取值

在这里插入图片描述

输入无效下标取值:

下标大于结点个数,无效
在这里插入图片描述
下标小于0,无效
在这里插入图片描述
下标等于0,无效
在这里插入图片描述

8.按结点值取其下标

输入值不存在,则给出提示

在这里插入图片描述

输入值存在,则给出下标

在这里插入图片描述

9.逆转双链表

逆转一次

在这里插入图片描述

再逆转一次

在这里插入图片描述

10.为双链表排序

在这里插入图片描述

11.清空双链表,保留其起源结点

从尾结点向首结点,依次释放每个结点

在这里插入图片描述

在双链表为空的情况下清空双链表,提示双链表为空

在这里插入图片描述

11.重新初始化双链表

①在双链表为空的情况下重新初始化双链表,先提示双链表为空,再释放起源结点,最后再重新初始化双链表。

在这里插入图片描述

②在双链表不为空的情况下,先释放链表中的有效结点,再释放起源结点,最后初始化双链表。

在这里插入图片描述

重新初始化后的双链表

在这里插入图片描述

12.在双链表为空情况下,无法进行的操作均会提示“DoubleLinkedList Is Empty!”

逆转

在这里插入图片描述

插入

在这里插入图片描述

排序

在这里插入图片描述

按下标取值

在这里插入图片描述

按值取下标

在这里插入图片描述

删除

在这里插入图片描述

修改

在这里插入图片描述

清空双链表

在这里插入图片描述

三、一字一句的血汗代码

干净利落无注释版:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define  OK 1
#define ERROR -1
#define status int
#define SPAN 35
#define VerticalGap 1

typedef struct doubleNode
{
	struct doubleNode * prior;
	int data;
	struct doubleNode * next;
}* DoubleNodePointer,DoubleNode;

DoubleNodePointer initialize();
void manifest(DoubleNodePointer origin);
status isEmpty(DoubleNodePointer origin);
void prompt(char* hint);
void inputData(char* hint,char* type,void* object);
status inverse(DoubleNodePointer origin);
status delete(DoubleNodePointer origin, int position);
void append(DoubleNodePointer origin, int value);
status insert(DoubleNodePointer origin, int position, int value);
DoubleNodePointer getNode(DoubleNodePointer origin, int position);
status locateNode(DoubleNodePointer origin, int value,int* position);
status modify(DoubleNodePointer origin, int position, int value);
status sort(DoubleNodePointer origin);
void clear(DoubleNodePointer origin);
void menu(char **menu, int length);
void duplicate(int size, char token);

void main()
{
	char *operatingMenu[] = {"DoubleLinkedList",
                             "1.reinitialize list", "2.present list", "3.append element",
                             "4.delete element", "5.insert element", "6.modify element",
                             "7.get element", "8.locate element", "9.sort", "10.inverse",
                             "11.clear list", "12.exit"};
    int length = sizeof(operatingMenu) / sizeof(char *);
    int indicator, position, value;
	DoubleNodePointer origin=initialize();
    while (1) 
	{
        system("cls");
        menu(operatingMenu, length);
		if(!isEmpty(origin))
		{
			printf("CURRENT TOTAL:\t%d\n",origin->data);
			manifest(origin);		
		}
		else
		{
			prompt("Data are required!\n");
		}	
        inputData("type in number:\t","%d",&indicator);
        switch (indicator) {
            case 1:
				clear(origin);
				free(origin);
				prompt("The Origin Node Is Released completely!\n");
				prompt("...\treinitializing\t...\n");
				origin=initialize();
                break;
            case 2:
                manifest(origin);
                system("pause");
                break;
            case 3:	
                inputData("VALUE:","%d",&value);
                append(origin, value);
                manifest(origin);
                system("pause");
                break;
            case 4:
                inputData("POSITION:","%d",&position);
                (delete(origin, position)== OK) ? manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 5:
                inputData("POSITION:","%d",&position);
                inputData("VALUE:","%d",&value);
                (insert(origin,position, value)==OK) ? manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 6:
               inputData("POSITION:","%d",&position);
                inputData("VALUE:","%d",&value);
				(modify(origin,position,value)==OK)?manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 7:
                inputData("POSITION:","%d",&position);
                DoubleNodePointer dnp=getNode(origin,position);
                dnp? printf("VALUE: %d\n", dnp->data) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 8:
                inputData("VALUE:","%d",&value);
                (locateNode(origin,value,&position)==OK) ? printf("POSITION:%d\n", position):prompt("check out and try again!\n");
                system("pause");
                break;
			case 9:
				(sort(origin)==OK)?manifest(origin):prompt("check out and try again!\n");
				system("pause");
				break;
            case 10:
                (inverse(origin)== OK) ? manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
			case 11:
				clear(origin);
                system("pause");
                break;
			case 12:
				clear(origin);
				free(origin);
				prompt("The Origin Node Is Released completely!\n");
				 system("pause");
				exit(-1);
				break;
        }
    }
}

status isEmpty(DoubleNodePointer origin)
{
	if(origin->next==NULL)
	{
		prompt("DoubleLinkedList Is Empty!\n");
		return 1;
	}
	return 0;
}

void inputData(char* hint,char* type,void* object)
{
	printf("%s",hint);
	scanf(type,object);
}

void prompt(char* hint)
{
	printf("%s",hint);
}

DoubleNodePointer initialize()
{
	DoubleNodePointer origin=(DoubleNodePointer)malloc(sizeof(DoubleNode));
	if(origin==NULL)
	{
		prompt("FAILED TO ALLOCATE MEMORY");
		exit(-1);
	}
	origin->prior=NULL;
	origin->next=NULL;
	origin->data=0;
	inputData("The Length Of DoubleLinkedList:","%d",&(origin->data));
	DoubleNodePointer transition=origin;
	DoubleNodePointer newborn;
	for(int i=0;i<origin->data;i++)
	{
		newborn=(DoubleNodePointer)malloc(sizeof(DoubleNode));
		if(newborn==NULL)
		{
			prompt("FAILED TO ALLOCATE MEMORY");
			exit(-1);
		}	
		printf("element\t%d:\t",i+1);
		scanf("%d",&(newborn->data));
		newborn->prior=transition;
		newborn->next=NULL;
		transition->next=newborn;
		transition=newborn;
	}
	return origin;
}

void manifest(DoubleNodePointer origin)
{
	if(isEmpty(origin))
		return;
	printf("{\t");
	while(origin->next!=NULL)
	{
		origin=origin->next;
		if(origin->next!=NULL)
			printf("%d\t",origin->data);
		else
			printf("%d",origin->data);
	}
	printf("\t}\n");
}

status inverse(DoubleNodePointer origin)
{
	if(isEmpty(origin))
		return ERROR;
	if(origin->data==1)
		return OK;
	DoubleNodePointer begin=origin->next;
	DoubleNodePointer end=origin;
	while(end->next!=NULL)
	{
		end=end->next;
	}
	int temp;
	while(begin->prior!=end && begin!=end)
	{
			temp=end->data;
			end->data=begin->data;
			begin->data=temp;
			begin=begin->next;
			end=end->prior;
	}
	return OK;
}

void append(DoubleNodePointer origin, int value)
{
	DoubleNodePointer newborn=(DoubleNodePointer)malloc(sizeof(DoubleNode));
	if(newborn==NULL)
	{
		prompt("FAILED TO ALLOCATE MEMORY\n");
		return;
	}
	origin->data=origin->data+1;
	newborn->data=value;
	newborn->next=NULL;
	for(;origin->next!=NULL;origin=origin->next);
	newborn->prior=origin;
	origin->next=newborn;
}

status delete(DoubleNodePointer origin, int position)
{
	if(isEmpty(origin))
		return ERROR;
	int* length=&(origin->data);
	int i;
	for(i=1;i<position && origin->next!=NULL;i++,origin=origin->next);
	if(i>position || origin->next==NULL)
	{															
		prompt("INVALID POSITION\n");
		return ERROR;
	}
	--*length;
	DoubleNodePointer destroyedNode=origin->next;
	int value=destroyedNode->data;
	origin->next=destroyedNode->next;
	
	if(destroyedNode->next!=NULL)
		destroyedNode->next->prior=origin;
	free(destroyedNode);
	printf("The element < %d > is deleted successfully!\n",value);
	return OK;
}

status insert(DoubleNodePointer origin, int position, int value)
{
	if(isEmpty(origin))
		return ERROR;
	DoubleNodePointer spring=origin;
	int i=1;
	for(;i<position && origin->next!=NULL;i++,origin=origin->next);
	if(i>position || origin->next==NULL)
	{
		prompt("INVALID POSITION\n");
		return ERROR;
	}
	spring->data++;
	DoubleNodePointer newborn=(DoubleNodePointer)malloc(sizeof(DoubleNode));
	newborn->data=value;
	newborn->prior=origin;
	newborn->next=origin->next;
	origin->next->prior=newborn;
	origin->next=newborn;
	return OK;
} 

DoubleNodePointer getNode(DoubleNodePointer origin, int position)
{
	if(isEmpty(origin))
		return NULL;
	int i=1;
	for(;i<position && origin->next!=NULL;i++,origin=origin->next);
	if(origin->next==NULL || i>position)
	{
		prompt("INVALID POSITION\n");
		return NULL;
	}
	return origin->next;
}

status locateNode(DoubleNodePointer origin, int value,int* position)
{
	*position=-1;
	if(isEmpty(origin))
		return ERROR;
	int i=1;
	for(;origin->next!=NULL && origin->next->data!=value;i++,origin=origin->next);
	if(origin->next==NULL)
	{
		prompt("There Is No Such Node!!!\n");
		return ERROR;
	}
	*position=i;
	return OK;
}

status modify(DoubleNodePointer origin, int position, int value)
{
	if(isEmpty(origin))
		return ERROR;
	if(position<1 || position>origin->data)
	{
		prompt("INVALID POSITION\n");
		return ERROR;
	}
	for(int i=0;i<position;i++,origin=origin->next);
	origin->data=value;
	prompt("The modification is completed!\n");
	return OK;
}

status sort(DoubleNodePointer origin)
{
	if(isEmpty(origin))
		return ERROR;
	if(origin->data==1)
		return OK;
	DoubleNodePointer begin=origin->next->next;
	DoubleNodePointer end=NULL;
	int rival;
	for(;begin!=NULL;begin=begin->next)
	{
		rival=begin->data;
		end=begin->prior;
		for(;end!=origin && rival<end->data;end=end->prior)
		{
			end->next->data=end->data;
		}
		end->next->data=rival;
	}
	prompt("Sorting is finished!\n");
	return OK;
}

void clear(DoubleNodePointer origin)
{
	if(isEmpty(origin))	
		return;
	DoubleNodePointer terminal=origin;
	DoubleNodePointer temporary=NULL;
	int value;
	for(;terminal->next!=NULL;terminal=terminal->next);
	while(terminal!=origin)
	{
		temporary=terminal;
		value=terminal->data;
		terminal=terminal->prior;
		free(temporary);
		printf("The element < %d > is released completely!\n",value);
		origin->data--;
	}
	origin->next=NULL;
}

void menu(char **origin, int length) 
{
    int span = SPAN;
    int gapLength;
    duplicate(span, '*');
    duplicate(1, '\n');
    for (int i = 0; i < length; ++i) {
        duplicate(1, '*');
        gapLength = span - (strlen(*(origin + i))) - 2;
        duplicate(gapLength / 2, ' ');
        printf("%s", origin[i]);
        if (gapLength % 2 == 0) {
            duplicate(gapLength / 2, ' ');
        } else {
            duplicate(gapLength / 2 + 1, ' ');
        }
        duplicate(1, '*');
        duplicate(VerticalGap, '\n');
    }
    duplicate(span, '*');
    duplicate(1, '\n');
}

void duplicate(int size, char token) {
    for (int i = 0; i < size; ++i) {
        printf("%c", token);
    }
}


呕心沥血有注释版:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define  OK 1
#define ERROR -1
#define status int
#define SPAN 35//菜单的水平跨度
#define VerticalGap 1//菜单的垂直间距,1就是一个换行,2就是两个换行

typedef struct doubleNode					//定义双向链表的结点
{
	struct doubleNode * prior;				//前指针域	
	int data;												//数据域
	struct doubleNode * next;					//后指针域
}* DoubleNodePointer,DoubleNode;	//DoubleNodePointer 等于struct doubleNode* ,DoubleNode等于struct doubleNode

DoubleNodePointer initialize();
//初始化双链表,
//由用户输入双向链表的长度,长度大于0,再依次输入每个结点的值,生成一个带起源结点的双向链表,并返回起源指针

void manifest(DoubleNodePointer origin);
//遍历双向链表的所有结点,不包括起源结点
//这里可能有人会很好奇,函数名不是说好的show吗?
//我之所以把函数名改了,是因为这个单词我接触到无数次了,内心有种抵触

status isEmpty(DoubleNodePointer origin);
//判断双向链表是否为空,若空返回1,否则返回0
//若起源结点后指针域为空,则双向链表为空,否则双向链表不为空

void prompt(char* hint);
//自己封装的一个函数,运用了字符串和指针的小知识,用来输出提示信息
//只需给函数形参传递一个字符串起始指针,或者直接传递字符串常量

void inputData(char* hint,char* type,void* object);
//自己封装的一个函数,运用了指针和字符串的小知识,用来输入数据
//形参主要有:
//				char* hint 数据输入前给出的提示信息,提示用户要输入什么数据。给其传递一个字符串常量,或者字符串起始指针即可
//				char* type 输入的数据是什么类型,事先定好。可以为其传递 %d、%c、%s、%f  %lf等
//				void* object	用什么变量接收数据,可为其传递一个变量的指针,该变量的类型要与type保持一致
//				举例1 char name[20];inputData("Your Name","%s",name);
//				举例2  float price;inputData("apple:","%f",&price);

status inverse(DoubleNodePointer origin);
//将双向链表中的所有有效结点进行头尾逆转,如:头尾逆转前 {14,13,2020,52,952,} 头尾逆转后 {952,52,2020,13,14}
//需要为函数形参传递双向链表的起源指针,函数返回值为双向链表逆转是否完成的状态值,OK或者ERROR
//若双向链表为空,返回ERROR;若双向链表只有一个有效结点,返回OK;
//若双向链表存在多个有效结点就进行一次逆转,返回OK

status delete(DoubleNodePointer origin, int position);
//删除双向链表中某一指定下标的结点
//需要为函数形参传递双向链表的起源指针,双向链表的结点下标,返回值为删除结点是否完成的状态值,OK或者ERROR
//若输入下标小于等于0,下标无效,返回ERROR;若输入下标大于有效结点个数,下标无效。返回ERROR
//若输入有效下标,则删除该下标对应的结点,返回OK

void append(DoubleNodePointer origin, int value);
//为双向链表追加新结点
//链表没有长度限度,只要你内存够用,随便追加结点
//需要为函数形参传递双向链表的起源指针,新结点的数据
//首先生成新结点,然后为其数据域和后指针域初始化,最后利用其前指针域与双向链表尾结点相互牵链

status insert(DoubleNodePointer origin, int position, int value);
//为双向链表插入一个新结点
//为函数形参传递起源指针,新结点插入的下标,新结点的数据
//首先判断双向链表是否为空,为空则无法进行插入新结点操作
//然后判断插入的下标是否为有效下标,若为有效下标则插入新结点,否则无法插入新结点,返回ERROR
//下标小于等于0、大于有效结点个数均为无效下标

DoubleNodePointer getNode(DoubleNodePointer origin, int position);
//按下标取结点
//为函数形参传递起源指针,结点下标,返回对应的结点
//若双向链表为空,返回NULL;若下标为无效下标,返回NULL。下标小于等于0、下标大于结点个数均视为无效
//下标有效时,返回对应的结点

status locateNode(DoubleNodePointer origin, int value,int* position);
//按值取结点下标
//需要给形参传递起源指针,结点数据,待赋值的下标变量指针,返回查询结点后的状态值 OK 或者 ERROR
//若双向链表为空,为下标变量赋值-1,返回ERROR;
//若结点存在,则为下标变量赋上对应的下标值 ,返回OK;否则为下标变量赋值-1,返回 ERROR

status modify(DoubleNodePointer origin, int position, int value);
//根据下标,修改对应结点的数据
//为函数形参传递起源指针,结点下标,结点数据,返回修改后的状态值 OK 或者 ERROR
//若双向链表为空,返回ERROR
//若输入的下标无效,返回ERROR
//下标有效,修改结点的数据,返回OK
status sort(DoubleNodePointer origin);
//为双向链表进行升序排序,返回排序后的状态值OK 或者 ERROR
//若双向链表为空,返回ERROR
//若双向链表的长度为1,返回OK
//若双向链表存在多个结点则进行排序,返回OK

void clear(DoubleNodePointer origin);
//清空双向链表,但保留起源结点,起源结点的后指针域置空

void menu(char **menu, int length);//动态地输出菜单
void duplicate(int size, char token);//给出一个整数size和一个字符型符号token,输出size个token,不换行

void main()
{
	char *operatingMenu[] = {"DoubleLinkedList",
                             "1.reinitialize list", "2.present list", "3.append element",
                             "4.delete element", "5.insert element", "6.modify element",
                             "7.get element", "8.locate element", "9.sort", "10.inverse",
                             "11.clear list", "12.exit"};
    int length = sizeof(operatingMenu) / sizeof(char *);
    int indicator, position, value;
	DoubleNodePointer origin=initialize();
    while (1) 
	{
        system("cls");
        menu(operatingMenu, length);
		if(!isEmpty(origin))
		{
			printf("CURRENT TOTAL:\t%d\n",origin->data);
			manifest(origin);		
		}
		else
		{
			prompt("Data are required!\n");
		}	
        inputData("type in number:\t","%d",&indicator);
        switch (indicator) {
            case 1:
				clear(origin);
				free(origin);
				prompt("The Origin Node Is Released completely!\n");
				prompt("...\treinitializing\t...\n");
				origin=initialize();
                break;
            case 2:
                manifest(origin);
                system("pause");
                break;
            case 3:	
                inputData("VALUE:","%d",&value);
                append(origin, value);
                manifest(origin);
                system("pause");
                break;
            case 4:
                inputData("POSITION:","%d",&position);
                (delete(origin, position)== OK) ? manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 5:
                inputData("POSITION:","%d",&position);
                inputData("VALUE:","%d",&value);
                (insert(origin,position, value)==OK) ? manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 6:
               inputData("POSITION:","%d",&position);
                inputData("VALUE:","%d",&value);
				(modify(origin,position,value)==OK)?manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 7:
                inputData("POSITION:","%d",&position);
                DoubleNodePointer dnp=getNode(origin,position);
                dnp? printf("VALUE: %d\n", dnp->data) : prompt("check out and try again!\n");
                system("pause");
                break;
            case 8:
                inputData("VALUE:","%d",&value);
                (locateNode(origin,value,&position)==OK) ? printf("POSITION:%d\n", position):prompt("check out and try again!\n");
                system("pause");
                break;
			case 9:
				(sort(origin)==OK)?manifest(origin):prompt("check out and try again!\n");
				system("pause");
				break;
            case 10:
                (inverse(origin)== OK) ? manifest(origin) : prompt("check out and try again!\n");
                system("pause");
                break;
			case 11:
				clear(origin);//清空链表,保留起源结点,以便后续操作
                system("pause");
                break;
			case 12:
				clear(origin);//清空链表所有有效节点
				free(origin);//起源结点再单独释放
				prompt("The Origin Node Is Released completely!\n");
                system("pause");
				exit(-1);//退出系统
				break;
        }
    }
}

status isEmpty(DoubleNodePointer origin)
{
	if(origin->next==NULL)//起源结点的next指针域无指向,那么双向链表即为空
	{
		prompt("DoubleLinkedList Is Empty!\n");
		return 1;
	}
	return 0;
}

void inputData(char* hint,char* type,void* object)
{
	printf("%s",hint);
	scanf(type,object);
}

void prompt(char* hint)
{
	printf("%s",hint);
}

DoubleNodePointer initialize()
{
	DoubleNodePointer origin=(DoubleNodePointer)malloc(sizeof(DoubleNode));
	//初始化起源结点,将其指针保存在指针变量origin中
	if(origin==NULL)//若起源指针为空的话,则分配内存失败,直接退出当前系统
	{
		prompt("FAILED TO ALLOCATE MEMORY");
		exit(-1);
	}
	origin->prior=NULL;//起源结点前指针域初始化为空
	origin->next=NULL;//起源结点后指针域初始化为空
	origin->data=0;//起源结点数据域初始化为0
	inputData("The Length Of DoubleLinkedList:","%d",&(origin->data));//起源结点的数据域存储有效结点的个数,该数由用户输入
	DoubleNodePointer transition=origin;
	DoubleNodePointer newborn;
	for(int i=0;i<origin->data;i++)
	{
		newborn=(DoubleNodePointer)malloc(sizeof(DoubleNode));//创建新结点
		if(newborn==NULL)//若内存分配失败,则弹出提示信息,不再往下执行
		{
			prompt("FAILED TO ALLOCATE MEMORY");
			exit(-1);
		}	
		printf("element\t%d:\t",i+1);//提示输入信息,
		scanf("%d",&(newborn->data));//由用户输入新结点的值
		newborn->prior=transition;//新结点与其前驱结点牵链
		newborn->next=NULL;//新结点的后指针域初始化为空
		transition->next=newborn;//新结点的前驱结点与其牵链
		transition=newborn;//将结点指针指向新结点,进行下一次循环
	}
	return origin;
}

void manifest(DoubleNodePointer origin)
{
	if(isEmpty(origin))//链表为空的话,后续操作不再执行
		return;
	printf("{\t");
	while(origin->next!=NULL)
	{
		origin=origin->next;
		if(origin->next!=NULL)
			printf("%d\t",origin->data);
		else
			printf("%d",origin->data);
	}
	printf("\t}\n");
}

status inverse(DoubleNodePointer origin)
{
	if(isEmpty(origin))
		return ERROR;
	if(origin->data==1)
		return OK;
	DoubleNodePointer begin=origin->next;//保存双向链表的首结点指针
	DoubleNodePointer end=origin;//事先将end置为起源结点指针
	while(end->next!=NULL)
	{
		end=end->next;//将end定位到双向链表的尾结点指针
	}
	int temp;
	while(begin->prior!=end && begin!=end)
	{
	//若结点总数为偶数,在首指针前进,尾指针后退相遇之后结束循环
	//若结点总数为奇数,首指针和尾指针达中间结点后结束循环
			temp=end->data;
			end->data=begin->data;
			begin->data=temp;
			begin=begin->next;//首指针向后继结点前进
			end=end->prior;//尾指针向前驱结点后退
	}
	return OK;
}

void append(DoubleNodePointer origin, int value)
{
	DoubleNodePointer newborn=(DoubleNodePointer)malloc(sizeof(DoubleNode));//创建新结点,并分配堆内存
	if(newborn==NULL)//若内存分配失败,则弹出提示信息,不再往下执行
	{
		prompt("FAILED TO ALLOCATE MEMORY\n");
		return;
	}
	origin->data=origin->data+1;//追加结点时,起源结点的数据域加1
	newborn->data=value;
	newborn->next=NULL;
	for(;origin->next!=NULL;origin=origin->next);//定位到双向链表的尾结点
	newborn->prior=origin;//为新结点与尾结点牵链
	origin->next=newborn;//为尾结点与新结点牵链
}

status delete(DoubleNodePointer origin, int position)
{
	if(isEmpty(origin))
		return ERROR;
	//从逻辑上,为双向链表所有结点人为设定下标。起源结点下标为0,首结点下标为1,第二个结点下标为2,,,
	//小于等于0的下标为非法下标,大于结点个数的下标为非法下标
	//有效结点的个数存储在起源结点的数据域
	int* length=&(origin->data);//获取起源结点的数据域指针
	int i;
	for(i=1;i<position && origin->next!=NULL;i++,origin=origin->next);//直接定位到第position-1个结点
	//要删除第position个结点,先瞧它前面的position-1个结点是否都存在
	if(i>position || origin->next==NULL)//这里i>position的意思是"若输入的元素下标position小于0,则i自然大于position"
	{															//若第position个结点不存在 ,则输入下标position非法
		prompt("INVALID POSITION\n");
		return ERROR;
	}
	--*length;//删除一结点,起源结点的数据域减1 
	DoubleNodePointer destroyedNode=origin->next;//事先保存待销毁结点指针,origin现为待销毁结点的前驱结点
	int value=destroyedNode->data;//事先保存待销毁结点的数据域值
	origin->next=destroyedNode->next;//待销毁结点的前驱结点与其后继结点牵链
	
	if(destroyedNode->next!=NULL)//若待销毁结点存在后继结点
		destroyedNode->next->prior=origin;//则待销毁结点的后继结点与其前驱结点牵链
	//若待销毁结点不存在后继结点,即待销毁结点为尾结点,
	//则直接释放待销毁结点,其前驱结点的后指针域直接赋值为尾指针的后指针域,尾指针的后指针域自身就是空
	free(destroyedNode);//释放待销毁结点
	printf("The element < %d > is deleted successfully!\n",value);
	return OK;
}

status insert(DoubleNodePointer origin, int position, int value)
{
	if(isEmpty(origin))
		return ERROR;
	//从逻辑上,为双向链表所有结点人为设定下标。起源结点下标为0,首结点下标为1,第二个结点下标为2,,,
	//插入结点时,小于等于0的下标为非法下标,大于等于结点个数的下标为非法下标
	DoubleNodePointer spring=origin;//将起源结点指针保存在指针变量spring中
	int i=1;
	for(;i<position && origin->next!=NULL;i++,origin=origin->next);//遍历到第position-1个结点
	if(i>position || origin->next==NULL)
	{
		prompt("INVALID POSITION\n");
		return ERROR;
	}
	spring->data++;//插入一个新结点后起源结点数据域加1
	DoubleNodePointer newborn=(DoubleNodePointer)malloc(sizeof(DoubleNode));//创建待插入结点,并分配堆内存
	newborn->data=value;//为待插入结点的数据域赋值
//----------------------------------------------------插入新结点
	newborn->prior=origin;//为待插入结点与其前驱结点牵链
	newborn->next=origin->next;//为待插入结点与其后继结点牵链
	origin->next->prior=newborn;//为待插入结点的后继结点与待插入结点牵链 //双链表插入结点的次序不断乱
	origin->next=newborn;//为待插入结点的前驱结点与待插入结点牵链
//----------------------------------------------------插入新结点结束
	return OK;
} 

DoubleNodePointer getNode(DoubleNodePointer origin, int position)
{
	if(isEmpty(origin))
		return NULL;
	int i=1;
	for(;i<position && origin->next!=NULL;i++,origin=origin->next);//定位到双向链表的第position-1个结点
	if(origin->next==NULL || i>position)
	{
		prompt("INVALID POSITION\n");
		return NULL;
	}
	return origin->next;
}

status locateNode(DoubleNodePointer origin, int value,int* position)
{
	*position=-1;
	if(isEmpty(origin))
		return ERROR;
	int i=1;
	for(;origin->next!=NULL && origin->next->data!=value;i++,origin=origin->next);
	//上述循环需要认真仔细的思考一番,才能体会其中味道
	//i变量用来确定循环链表中的下标,
	//当结点指针指向首结点时,i=1;当结点指针指向第二个结点时,i=2;i当结点指针指向第三个结点时,i=3;...
	//请注意循环内的结点都用其前驱结点表示的。i=1,1号结点用起源结点表示;i=2,2号结点用1号结点表示;...
	//假设我们有六个有效结点,起源结点算在内,那么按照上述规律
	//4号结点,必是用3号结点表示,i=4
	//5号结点,即尾结点,必是用4号结点表示,i=5
	//若继续执行,6号结点,必是5号结点表示,
	//但5号结点已经是尾结点了,所以5号结点的后指针域为NUll,无法推算出6号结点,退出循环

	if(origin->next==NULL)
	{
		prompt("There Is No Such Node!!!\n");
		return ERROR;
	}
	*position=i;
	return OK;
}

status modify(DoubleNodePointer origin, int position, int value)
{
	if(isEmpty(origin))
		return ERROR;
	if(position<1 || position>origin->data)
	{
		prompt("INVALID POSITION\n");
		return ERROR;
	}
	for(int i=0;i<position;i++,origin=origin->next);//定位到第position个结点
	origin->data=value;
	prompt("The modification is completed!\n");
	return OK;
}

status sort(DoubleNodePointer origin)//选用插入排序
{
	if(isEmpty(origin))
		return ERROR;
	if(origin->data==1)
		return OK;
	DoubleNodePointer begin=origin->next->next;//从第2个结点开始,进行插入排序
	//默认第一个结点就是排序的基准,
	//第二个结点与第一个结点相比较,进行一次排序,
	//第三个结点与第二个结点、第一个结点相比较,进行一次排序
	//第四个结点与第三个结点、第二个结点、第一个结点相比较,进行一次排序,
	DoubleNodePointer end=NULL;
	int rival;
	for(;begin!=NULL;begin=begin->next)
	{
		rival=begin->data;//将待排序结点的数据域值事先保存起来
		end=begin->prior;//将待排序结点的前驱结点保存起来
		for(;end!=origin && rival<end->data;end=end->prior)
		//从其前驱结点往回倒,在倒的过程中,每个结点与待排序结点进行比较
		{
			end->next->data=end->data;
		}
		end->next->data=rival;//这一步需要认真思考,思考的点在循环条件rival<end->data
	}
	prompt("Sorting is finished!\n");
	return OK;
}

void clear(DoubleNodePointer origin)//清空链表,保留起源结点
{
	if(isEmpty(origin))	
		return;
	//双链表的清空和单链表的清空完全相同,可以把单链表的清空代码照抄过来,功能实现简单地不要不要的了
	//为了突显出双链表的前驱和后继指针域的属性,双链表的清空就从其尾结点向首结点遍历,逐个进行释放
	DoubleNodePointer terminal=origin;
	DoubleNodePointer temporary=NULL;
	int value;
	for(;terminal->next!=NULL;terminal=terminal->next);//定位到双向链表的尾结点
	while(terminal!=origin)
	{
		temporary=terminal;//记下当前结点
		value=terminal->data;//记下当前结点数据域值
		terminal=terminal->prior;//记下当前结点的前驱结点
		free(temporary);//释放当前结点
		printf("The element < %d > is released completely!\n",value);
		origin->data--;//起源结点数据域值减1
	}
	origin->next=NULL;//起源结点的后指针域置空
}

void menu(char **origin, int length) 
{
    int span = SPAN;
    int gapLength;
    duplicate(span, '*');
    duplicate(1, '\n');
    for (int i = 0; i < length; ++i) {
        duplicate(1, '*');
        gapLength = span - (strlen(*(origin + i))) - 2;
        duplicate(gapLength / 2, ' ');
        printf("%s", origin[i]);
        if (gapLength % 2 == 0) {
            duplicate(gapLength / 2, ' ');
        } else {
            duplicate(gapLength / 2 + 1, ' ');
        }
        duplicate(1, '*');
        duplicate(VerticalGap, '\n');
    }
    duplicate(span, '*');
    duplicate(1, '\n');
}

void duplicate(int size, char token) {
    for (int i = 0; i < size; ++i) {
        printf("%c", token);
    }
}


三、总结

这篇博客,算是我有史以来用时间最长的一篇博客了,内容比较详细丰富。在写博客和写代码注释的过程中,培养了自己的耐心和毅力,锻炼了自己的文字表达能力,还发现了代码中存在的些许逻辑错误,顿时感悟良多。这性子还是太急了,慢一点,不要着急,时间都来得及!如有发现代码中存在的错误,欢迎指正和批评,我一定及时纠正!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值