数据结构练习题:
用双向循环链表实现顺序递增存储若干自然数,比如输入一个整数10,则建立一个双向循环链表,里面的每个节点分别存放1,2,3,4,5,6,7,8,9,10,然后通过某些操作,将其重新排列成1,3,5,7,9,10,8,6,4,2,即奇数升序偶数降序,并在屏幕上打印出来。
相信你已经掌握了“双向循环链表”的相关结构知识,以及其“增删改查”的基本工作方式。接下来本人将会直接分析这道题目。
首先看题目的前半段:“用双向循环链表实现顺序递增存储若干自然数,比如输入一个整数10,则建立一个双向循环链表,里面的每个节点分别存放1,2,3,4,5,6,7,8,9,10。”
前半段题目要求对一个头节点,进行连续的“尾插”尾插,输入的内容是1到一个你自己设定的最大正整数。所有用for()循环函数连续“尾插”即可。
//下面的代码只是完整代码的摘要,文章最后面会给出完整代码
/**
***********************************
*@brief 尾插_自动生成从1到最大值的链表
*@param p : 头节点
*@param max : 生成数字最大值
*@retval None
***********************************
*/
void link_add_tail_auto(plink_t p,int max)
{
int data;
for (data = 1; data <= max; data++)
{
link_add_tail(p,data);
}
}
/**
***********************************
*@brief 尾插
*@param p : 头节点
*@param d : 数据
*@retval None
***********************************
*/
void link_add_tail(plink_t p,datatype d)
{
//创建新的节点
plink_t node = NULL;
link_init(&node);
if(node==NULL)
return;
node->data = d;
//将新的节点插在头的前面
insert_forward(p,node);
}
/**
***********************************
*@brief 一个节点node插在p的前面
*@param p node : 节点
*@retval None
***********************************
*/
static void insert_forward(plink_t p,plink_t node)
{
node->next = p;
node->prev = p->prev;
p->prev->next = node;
p->prev = node;
}
typedef int datatype;
typedef struct link{
datatype data;
struct link *prev;
struct link *next;
} link_t,*plink_t;
再来看题目的后半段:“每个节点分别存放1,2,3,4,5,6,7,8,9,10,然后通过某些操作,将其重新排列成1,3,5,7,9,10,8,6,4,2,即奇数升序偶数降序,并在屏幕上打印出来。”
进行“奇数升序,偶数降序”操作的最简单办法是:用一个指针从头节点逆向读取链表数据,将读取到的偶数节点进行“尾插”操作。即插入到头节点的前面。
“读取用的指针”从链表的尾部开始移动,“剪切用的指针”不断指向“读取用的指针”。
“读取用的指针”往前移动一位。
“剪切用的指针”的数据是偶数就进行尾插,插入头节点的前面。是奇数就直接跳过,不操作。
“读取用的指针”指到头节点就停下来。
//下面的代码只是完整代码的摘要,文章最后面会给出完整代码
/**
***********************************
*@brief 排序_奇数升序,偶数降序
*@param p : 头节点
*@retval None
***********************************
*/
void sort_rise_down(plink_t p)
{
plink_t head_p = p;
plink_t read_p = p->prev;
plink_t cut_p = NULL;
while (read_p!=head_p)
{
cut_p = read_p;
read_p = read_p->prev;
if(0 == (cut_p->data % 2))/* 判断是否为偶数 */
{
cut_to_tail(head_p,cut_p);
}
display(head_p);
}
}
/**
***********************************
*@brief 剪贴节点_尾插
*@param p : 头节点
*@param node : 需要剪贴并尾插的节点
*@retval None
***********************************
*/
void cut_to_tail(plink_t p,plink_t node)
{
cut_node(node);
insert_forward(p,node);
}
/**
***********************************
*@brief 剪切
*@param node : 节点
*@retval None
***********************************
*/
static void cut_node(plink_t node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
}
/**
***********************************
*@brief 一个节点node插在p的前面
*@param p node : 节点
*@retval None
***********************************
*/
static void insert_forward(plink_t p,plink_t node)
{
node->next = p;
node->prev = p->prev;
p->prev->next = node;
p->prev = node;
}
运行一下程序看看效果。
下面放出所有代码。
复制完成之后更改相对应的文件名,然后编译代码。
我这边的运行环境是Ubuntu18,使用编译命令“gcc link.c main.c -o app -Wall -O0 -g”。
头文件代码
//文件名:link.h
#ifndef __LINK_H
#define __LINK_H
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct link{
datatype data;
struct link *prev;
struct link *next;
} link_t,*plink_t;
extern void link_init(plink_t *p);
extern void link_add_head(plink_t p,datatype d);
extern void link_add_tail(plink_t p,datatype d);
extern void link_del(plink_t p,datatype d);
extern void link_update(plink_t p,datatype old,datatype new);
extern void display(plink_t p);
extern void pre_display(plink_t p);
extern void link_add_tail_node(plink_t p,plink_t node);
extern void link_add_tail_rise(plink_t p,datatype d);
extern void link_add_tail_auto(plink_t p,int max);
extern void sort_rise_down(plink_t p);
extern void sort_rise_down_2(plink_t p);
extern void cut_to_tail(plink_t p,plink_t node);
extern void cut_to_head(plink_t p,plink_t node);
#endif
自定义函数代码文件
//文件名:link.c
/* 双向循环链表*/
#include "link.h"
/**
***********************************
*@brief 一个节点node插在p的后面
*@param p node : 节点
*@retval None
***********************************
*/
static void insert_behind(plink_t p,plink_t node)
{
node->next = p->next;
node->prev = p;
p->next->prev = node;
p->next = node;
}
/**
***********************************
*@brief 一个节点node插在p的前面
*@param p node : 节点
*@retval None
***********************************
*/
static void insert_forward(plink_t p,plink_t node)
{
node->next = p;
node->prev = p->prev;
p->prev->next = node;
p->prev = node;
}
/**
***********************************
*@brief 剪切
*@param node : 节点
*@retval None
***********************************
*/
static void cut_node(plink_t node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
}
/**
***********************************
*@brief 新旧节点的替换
*@param old 旧节点
*@param new 新节点
*@retval None
***********************************
*/
static void replace(plink_t old,plink_t new)
{
new->next = old->next;
new->prev = old->prev;
new->next->prev = new;
new->prev->next = new;
}
/**
***********************************
*@brief 初始化 (创建头节点)
*@param p : 头节点
*@retval None
***********************************
*/
void link_init(plink_t *p)
{
*p=(plink_t)malloc(sizeof(link_t));
if(*p==NULL){
perror("malloc error");
return;
}
(*p)->prev = (*p);
(*p)->next = (*p);
}
/**
***********************************
*@brief 头插
*@param p : 头节点
*@param d : 数据
*@retval None
***********************************
*/
void link_add_head(plink_t p,datatype d)
{
//创建新的节点
plink_t node = NULL;
link_init(&node);
if(node==NULL)
return;
node->data = d;
//将新的节点插在头的后面
insert_behind(p,node);
}
/**
***********************************
*@brief 尾插
*@param p : 头节点
*@param d : 数据
*@retval None
***********************************
*/
void link_add_tail(plink_t p,datatype d)
{
//创建新的节点
plink_t node = NULL;
link_init(&node);
if(node==NULL)
return;
node->data = d;
//将新的节点插在头的前面
insert_forward(p,node);
}
/**
***********************************
*@brief 删除
*@param p : 头节点
*@param d : 需要删除的数据
*@retval None
***********************************
*/
void link_del(plink_t p,datatype d)
{
plink_t head = p;
plink_t node = NULL;
//找到该节点
while(p->next!=head){
node = p->next;
if(node->data == d){
//剪切并删除该节点
cut_node(node);
node->prev = node;
node->next = node;
free(node);
continue;
}
p = p->next;
}
}
/**
***********************************
*@brief 更新
*@param p : 头节点
*@param old : 旧节点
*@param new : 新节点
*@retval None
***********************************
*/
void link_update(plink_t p,datatype old,datatype new)
{
plink_t head = p;
plink_t node = NULL;
plink_t new_node = NULL;
//遍历寻找old
while(p->next!=head){
node = p->next;
if(node->data == old){
//找到后,创建新的节点new
link_init(&new_node);
if(new_node==NULL)
return;
new_node->data = new;
//新的节点替换old
replace(node,new_node);
//释放old
node->prev = node;
node->next = node;
free(node);
}
p = p->next;
}
}
/**
***********************************
*@brief 正序遍历
*@param p : 头节点
*@retval None
***********************************
*/
void display(plink_t p)
{
plink_t head = p;
printf("正序遍历结果为:");
while(p->next!=head){
p = p->next;
printf("%d ",p->data);
}
printf("\n");
}
/**
***********************************
*@brief 逆序遍历
*@param p : 头节点
*@retval None
***********************************
*/
void pre_display(plink_t p)
{
plink_t head = p;
printf("逆序遍历结果为:");
while(p->prev!=head){
p = p->prev;
printf("%d ",p->data);
}
printf("\n");
}
/**
***********************************
*@brief 尾插_节点
*@param p : 头节点
*@param node : 要插入的节点
*@retval None
***********************************
*/
void link_add_tail_node(plink_t p,plink_t node)
{
//将新的节点插在头的前面
insert_forward(p,node);
}
/**
***********************************
*@brief 尾插_自动排序递增
*@param p : 头节点
*@param d : 生成数字的最大值
*@retval None
***********************************
*/
void link_add_tail_rise(plink_t p,datatype d)
{
int flag = 0;
//创建新的节点
plink_t node = NULL;
link_init(&node);
if(node==NULL)
return;
node->data = d;
plink_t head_p = p;
while(p->next!=head_p)
{
p = p->next;
printf("d:%d ",d);
printf("p->data:%d \n",p->data);
if(d < p->data)
{
printf("break\n");
//将新的节点插在p节点的前面
insert_forward(p,node);
printf("前插:%d\n",d);
flag = 1;
break;
}
}
if (p->next == head_p && flag == 0)
{
//将新的节点插在p节点的后面
insert_behind(p,node);
printf("后插:%d\n",d);
}
}
/**
***********************************
*@brief 尾插_自动生成从1到最大值的链表
*@param p : 头节点
*@param max : 生成数字最大值
*@retval None
***********************************
*/
void link_add_tail_auto(plink_t p,int max)
{
int data;
for (data = 1; data <= max; data++)
{
link_add_tail(p,data);
}
}
/**
***********************************
*@brief 剪贴节点_尾插
*@param p : 头节点
*@param node : 需要剪贴并尾插的节点
*@retval None
***********************************
*/
void cut_to_tail(plink_t p,plink_t node)
{
cut_node(node);
insert_forward(p,node);
}
/**
***********************************
*@brief 剪贴节点_头插
*@param p : 头节点
*@param node : 需要剪贴并头插的节点
*@retval None
***********************************
*/
void cut_to_head(plink_t p,plink_t node)
{
cut_node(node);
insert_behind(p,node);
}
/**
***********************************
*@brief 排序_奇数升序,偶数降序
*@param p : 头节点
*@retval None
***********************************
*/
void sort_rise_down(plink_t p)
{
plink_t head_p = p;
plink_t read_p = p->prev;
plink_t cut_p = NULL;
while (read_p!=head_p)
{
cut_p = read_p;
read_p = read_p->prev;
if(0 == (cut_p->data % 2))/* 判断是否为偶数 */
{
cut_to_tail(head_p,cut_p);
}
display(head_p);
}
}
/**
***********************************
*@brief 排序2_奇数升序,偶数降序
*@param p : 头节点
*@retval None
***********************************
*/
void sort_rise_down_2(plink_t p)
{
plink_t head_p = p;
plink_t read_p = p;
plink_t cut_p = p;
plink_t flag_p = p;
while (read_p->next!=flag_p)
{
read_p = read_p->next;
if(0 == (read_p->data % 2))/* 判断是否为偶数 */
{
cut_p = read_p;
read_p = read_p->prev;
cut_to_tail(flag_p,cut_p);
flag_p = cut_p;
}
display(head_p);
}
}
运行文件
//文件名:main.c
#include "link.h"
int main(void)
{
plink_t head_p = NULL;
link_init(&head_p);
if(head_p==NULL)
return -1;
printf("head_p:%p\n",head_p);
int max;
printf("请输入一个正整数:");
scanf("%d",&max);
printf("开始自动尾插\n");
link_add_tail_auto(head_p,max);
display(head_p);
printf("执行“奇数升序,偶数降序”操作\n");
sort_rise_down(head_p);
display(head_p);
return 0;
}