双链表 (double link)
最近学习了链表。相比较单链表的只有一个后继结点的小清新风格而言。双链表中前驱结点和后继结点就稍显麻烦。
本人在学习双链表的过程中就被满天飞的前驱后继指针弄的一个头两个大。
不过等过了这段磨合期之后,又慢慢觉得这风骚的前驱后继指针可爱极了。
特别是在双链表的一些基本操作里,只需要申明一个头结点的“影分身”指针就可以根据前驱亦或后继在链表里畅行无阻了。。
下面是双链表的一些基本操作。包括 链表的初始化、创建、打印、插入、删除、查找以及销毁。
#include <stdio.h>
#include <stdlib.h>
typedef struct _D_Node //链表中的元素。
{
int data;
struct _D_Node *pre;
struct _D_Node *next;
}Node,*pNode;
typedef struct _D_List //链表
{
struct _D_Node Head;//定义一个头结点 而不是一个头指针。
struct _D_Node *Tail;
unsigned size;
}List,*pList;
{
mylist->Head.next = NULL;
mylist->Head.pre = NULL;
mylist->size = 0;
mylist->Head.data = 0;
mylist->Tail = NULL;
}
int CreatMyList (pList mylist) //创建一个双链表。
{
int i;
pNode tmp = NULL; //申明一个结点用以代替头指针在链表里面跳。
while (scanf("%d",&i))
{
pNode ptr = (pNode)malloc (sizeof (Node));
if (mylist->Head.next == NULL) //当只有一个头结点的时候。。
{
mylist->Head.next = ptr;
mylist->size++;
ptr->data = i;
ptr->next = NULL;
ptr->pre = &mylist->Head; //将头结点的地址赋给 ptr 的 前驱指针。 & 取地址运算符。
tmp = mylist->Head.next;
}
else
{
tmp->next = ptr;
mylist->size++;
ptr->data = i;
ptr->next = NULL;
ptr->pre = tmp;
tmp = tmp->next; // 让 tmp 跳到链表的最后一个结点处。
}
}
return mylist->size; //返回结点个数。
}
int Display (pList mylist) //打印链表。
{
pNode tmp = mylist->Head.next;
if (mylist->Head.next == NULL) //如果只有一个头结点的时候。
{
return -1;
}
else
{
while (tmp != NULL)
{
printf("%d\n",tmp->data);
tmp = tmp->next;
}
}
return 0;
}
int Insertion (pList mylist, unsigned int num) //在第 num 个结点后插入一个结点。
{
pNode tmp = NULL; // 声明一个头结后继指针的"影分身"。使之代替头指针在该链表里面跳转。
pNode ptr = (pNode)malloc (sizeof (Node)); //申请一个待插结点的存储空间。并进行初始化。
ptr->data = 666888;
ptr->next = NULL;
ptr->pre = NULL;
if (num < 0 || num > mylist->size)
{
return -1;
}
else if (0 == num) //插入头结点的后面。。即首个结点。
{
ptr->pre = &mylist->Head;
ptr->next = mylist->Head.next;
mylist->Head.next->pre = ptr;
mylist->Head.next = ptr;
}
else
{
tmp = mylist->Head.next;
while (num - 1)
{
tmp = tmp->next; // 将 tmp 跳到被插结点的前一个处。
num--;
}
ptr->pre = tmp;
ptr->next = tmp->next;
tmp->next->pre = ptr;
tmp->next = ptr;
}
return 0;
}
int Remove1(pList mylist,int value) //删除对应 value值 的结点。
{
pNode ptr = mylist->Head.next; // 影分身!!!!
while (ptr->data != value && ptr != NULL) //当 分身的值和value不匹配且分身没跳出链表时。
{
ptr = ptr->next;
}
ptr->pre->next = ptr->next;
ptr->pre->next->pre = ptr->pre;
free (ptr);
return 0;
}
int Remove2(pList mylist,unsigned int num) //删除第 num 个结点元素。
{
pNode ptr = mylist->Head.next; // 华丽的影分身。
if (num <0 || num > mylist->size)
{
return -1;
}
else
{
while (num - 1) // 分身向后跳 num - 1 个结点。
{
ptr = ptr->next;
num--;
}
ptr->pre->next = ptr->next;
ptr->pre->next->pre = ptr->pre;
free (ptr);
return 0;
}
}
int Find (pList mylist,unsigned int num) //查找第 num 个结点。
{
pNode ptr = &mylist->Head; // 这次分身指向的是头结点噢。因为想强调下 & 取地址运算符 = =。
if (num < 0 || num > mylist->size)
{
return -1;
}
else
{
while (num) //分身欢快的向后寻找第 num 个结点去了。
{
ptr = ptr->next;
num--;
}
printf ("the data you want is %d\n",ptr->data); //Bingo !!!!
}
return 0;
}
int DestoryList (pList mylist) // 销毁它!!!
{
if (mylist->Head.next == NULL) //如果只是个空链表(只有表头),就...放了它吧。
{
return -1;
}
else
{
pNode ptr = mylist->Head.next; //咳咳。这货又来。这次是做被 free()的目标。
while (mylist->Head.next != NULL)
{
mylist->Head.next = ptr->next;
mylist->Head.next->pre = ptr->pre;
free (ptr);
ptr = mylist->Head.next;
}
return 0;
}
}
int main(void) {
List dm;
Initialize (&dm);
printf("the size is %d\n",CreatMyList (&dm));
Insertion (&dm,3);
Remove1 (&dm,1);
Remove2 (&dm,7);
Display (&dm);
Find (&dm,3);
DestoryList (&dm);
Display (&dm);
return 0;
}