严蔚敏数据结构 P36——算法2.18(前置程序)
双向循环链表的建立
利用前置指针和后置指针进行正序和逆序输出
/*
严蔚敏数据结构 P36——算法2.18(前置程序)
双向循环链表的建立
*/
//头文件
#include<stdio.h>
#include<stdlib.h>
//结构
typedef struct st //双向链表st的结构
{
int date; //数据域
struct st *TOP; //前置指针域
struct st *NEXT; //后置指针域
}list, *sqlist;
//函数区
void build_list(sqlist List) //建立双向链表
{
int len, i, date;
sqlist NEW = NULL, temp = NULL; //NEW用于指向新开辟的结点,temp用于指向新开辟结点的前一个结点
printf("输入需要开辟的元素个数:"); //提醒用户输入相对于的操作
scanf_s("%d", &len); //记录用户需要开辟的元素个数
temp = List; //将头指针的权限赋给temp
for (i = 1;i <= len;i++)
{
printf("请输入第%d个结点的元素:", i); //提醒用户输入相对于的操作
scanf_s("%d", &date); //记录用户每次结点开辟的个数
NEW = (sqlist)malloc(sizeof(list)); //开辟新的结点并用NEW指向
if (!NEW)
{
printf("程序建立开辟结点有误,检查程序!\n"); //显示错误信息
exit(1); //退出程序
}
NEW->date = date; //将数据放进新结点的数据域
NEW->TOP = temp; //使新开辟结点的前置结点挂在前一个结点
temp->NEXT = NEW; //将新结点挂在前一个结点
temp = NEW; //将操作指针指向下一个结点
}
NEW->NEXT = List; //将尾结点的后置指针挂在头结点
List->TOP = NEW; //将头结点的前置指针指向尾结点
}
void prin(sqlist List) //遍历双向链表
{
sqlist p = NULL; //定义操作指针
p = List->NEXT; //将p指向第一个元素
printf("双向链表的值为:"); //显示信息
do
{
printf("%d ", p->date); //输出元素
p = p->NEXT; //p指向下一个元素
} while (p != List); //如果p不为头结点(因为双向链表的尾结点指向头结点)
printf("\n");
}
void against_prin(sqlist List) //遍历双向链表(尾结点遍历)(用于判断双向链表)
{
sqlist p = NULL; //定义操作指针
p = List->TOP; //将p指向第一个元素
printf("双向链表逆序的值为:"); //显示信息
do
{
printf("%d ", p->date); //输出元素
p = p->TOP; //p指向上一个元素
} while (p != List); //如果p不为头结点(因为双向链表的尾结点指向头结点)
printf("\n");
}
//主函数
int main(void)
{
sqlist List = NULL; //双向链表的头指针
List = (sqlist)malloc(sizeof(list)); //开辟头结点,并用List头指针指向
if (!List)
{
printf("程序建立开辟结点有误,检查程序!\n"); //显示错误信息
exit(1); //退出程序
}
List->NEXT = NULL; //将后置指针挂起
List->TOP = NULL; //将前置指针挂起
build_list(List); //建立双向链表
prin(List); //遍历双向链表(正常遍历)
against_prin(List); //遍历双向链表(尾结点遍历)(用于判断双向链表反方向)(逆序输出)
return 0;
}
双向链表的结构:
typedef struct st //双向链表st的结构
{
int date; //数据域
struct st *TOP; //前置指针域
struct st *NEXT; //后置指针域
}list, *sqlist;
struct st *TOP是各个结点的前置指针,用于指向前一个结点
struct st *NEXT是各个结点的后置指针,用于指向后一个结点
核心算法(建立双向链表):
void build_list(sqlist List) //建立双向链表
{
int len, i, date;
sqlist NEW = NULL, temp = NULL; //NEW用于指向新开辟的结点,temp用于指向新开辟结点的前一个结点
printf("输入需要开辟的元素个数:"); //提醒用户输入相对于的操作
scanf_s("%d", &len); //记录用户需要开辟的元素个数
temp = List; //将头指针的权限赋给temp
for (i = 1;i <= len;i++)
{
printf("请输入第%d个结点的元素:", i); //提醒用户输入相对于的操作
scanf_s("%d", &date); //记录用户每次结点开辟的个数
NEW = (sqlist)malloc(sizeof(list)); //开辟新的结点并用NEW指向
if (!NEW)
{
printf("程序建立开辟结点有误,检查程序!\n"); //显示错误信息
exit(1); //退出程序
}
NEW->date = date; //将数据放进新结点的数据域
NEW->TOP = temp; //使新开辟结点的前置结点挂在前一个结点
temp->NEXT = NEW; //将新结点挂在前一个结点
temp = NEW; //将操作指针指向下一个结点
}
NEW->NEXT = List; //将尾结点的后置指针挂在头结点
List->TOP = NEW; //将头结点的前置指针指向尾结点
}
可以看到:
NEW->TOP = temp; //使新开辟结点的前置结点挂在前一个结点
temp->NEXT = NEW; //将新结点挂在前一个结点
NEW->TOP = temp;使新开辟结点的前置结点挂在前一个结点
temp->NEXT = NEW;将新结点挂在前一个结点
temp = NEW;将操作指针指向下一个结点
重点:正序输出
void prin(sqlist List) //遍历双向链表
{
sqlist p = NULL; //定义操作指针
p = List->NEXT; //将p指向第一个元素
printf("双向链表的值为:"); //显示信息
do
{
printf("%d ", p->date); //输出元素
p = p->NEXT; //p指向下一个元素
} while (p != List); //如果p不为头结点(因为双向链表的尾结点指向头结点)
printf("\n");
}
逆序输出:
void against_prin(sqlist List) //遍历双向链表(尾结点遍历)(用于判断双向链表)
{
sqlist p = NULL; //定义操作指针
p = List->TOP; //将p指向第一个元素
printf("双向链表逆序的值为:"); //显示信息
do
{
printf("%d ", p->date); //输出元素
p = p->TOP; //p指向上一个元素
} while (p != List); //如果p不为头结点(因为双向链表的尾结点指向头结点)
printf("\n");
}
两种很相像,唯一不同的是一个是利用
P = List->NEXT;
P->NEXT从头往后遍历
逆序则是
P = List->TOP;
P->TOP从后往前遍历
正常遍历:
p = List->NEXT; //将p指向第一个元素
do
{
printf("%d ", p->date); //输出元素
p = p->NEXT; //p指向下一个元素
} while (p != List);
反向遍历:
p = List->TOP; //将p指向第一个元素
do
{
printf("%d ", p->date); //输出元素
p = p->TOP; //p指向上一个元素
} while (p != List);
运行结果:注意观察
感谢观看
再次感谢~