本周学习了数据结构中的几个部分,分别为:顺序表,链表,栈,队列。
顺序表
顺序存储就是将数据存储到一片连续的内存中,在C语言环境下,可以 是具名的栈数组,或者是匿名的堆数组。
顺序表的设计:
1.顺序表设计
一般而言,为了方便操作顺序表,需要一个专门管理顺序表 的"管理结构体",管理结构体一般会包括:
(1) 顺序表总容量
(2) 顺序表当前最末元素下标位置
(3) 顺序表指针
相关代码:
typedef struct
{
int capacity; // 顺序表容量
int last; // 最末元素下标
int * data; // 顺序表,以整型数据为例
}sequenceList;
顺序表的操作较为简单,下面以一个练习为例来讲解顺序表的操作
顺序表练习
创建一个顺序表,并从键盘接收数字输入,将输入的正整数按从小到大的顺序插入顺序表,并在输入负整数的时候将其绝对值数据删除。每次输入后,将顺序表的内容打印到屏幕上。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
typedef struct Seqlist
{
datatype last;//最末元素下标
datatype *data;//存储顺序表的数据
datatype capacity;//顺序表容量
} seqlist;
//顺序表的初始化
seqlist *init_seqlist(int cap)
{
seqlist *s = malloc(sizeof(seqlist));
if (s == NULL)
return NULL;
s->capacity = cap;
s->last = -1;
s->data = malloc(sizeof(datatype) * cap);
if (s->data == NULL)
{
free(s);
return NULL;
}
return s;
}
//判断顺序表是否为满
bool isfull(seqlist *list)
{
if (list->capacity == list->last + 1)
return true;
return false;
}
//插入数据
bool insert_data(int data, seqlist *list)
{
if(list==NULL)
return false;
// 判断列表是否全满,若全满则插入不了
if (isfull(list))
return false;
list->data[++list->last] = data;
return true;
}
//判断顺序表是否为空
bool isempty(seqlist *list)
{
if (list->last == -1)
{
perror("");
return true;
}
return false;
}
//打印顺序表
void printlist(seqlist *list)
{
if (isempty(list))
return;
for (int i = 0; i <= list->last; i++)
{
printf("%d\t", list->data[i]);
}
printf("\n");
}
//删除顺序表中的元素
void delet_data(seqlist *list, int a)
{
if(isempty(list))
return;
for (int i = 0; i < list->last; i++)
{
if(list->data[i] == -a)
{
for (int j = i; j < list->last; j++)
{
list->data[j]=list->data[j+1];
}
}
}
list->last--;
}
// 销毁顺序表
void destory(seqlist *list)
{
free(list->data);
list->data=NULL;
free(list);
list=NULL;
}
int main()
{
int cap = 0;
scanf("%d", &cap);
// 初始化链表
seqlist *list = init_seqlist(cap);
int buf[5]={0};
printf("请输入元素:");
for (int i = 0; i < 5; i++)
{
scanf("%d", &buf[i]);
}
//对输入元素进行排序
for(int j=0;j<4;j++)
{
for(int k=0;k<4-j;k++)
{
if(buf[k]>buf[k+1])
{
int temp=buf[k];
buf[k]=buf[k+1];
buf[k+1]=temp;
}
}
}
// 插入元素
for(int m=0;m<5;m++)
{
insert_data(buf[m],list);
}
// 打印顺序表
printlist(list);
// 删除元素
int a;
printf("请输入要删除的元素的负数:");
scanf("%d", &a);
delet_data(list, a);
// 打印顺序表
printlist(list);
//销毁顺序表
destory(list);
return 0;
}
顺序表的优缺点
1.优点
不需要多余的信息来记录数据的关系,存储密度高
所有数据顺序存储在一片连续的内存中,支持立即访问任意一个随机数 据,比如上述顺序表中第i个节点是s->data[i]
2.缺点
插入、删除时需要保持数据的物理位置反映其逻辑关系,需要成片移动数据
当数据节点较多时,需要一整片较大的连续内存空间
当数据节点数量变化剧烈时,内存的释放和分配不灵活
链表
链式存储的线性表,简称链表。
链表其实是由一个或者多个结构体通过指针指向的关系构成
我们把每个结构体的变量称为节点,节点里面由两个成员组成
一个是数据域,另外一个是指针域,指针域是用于存放下一个节点的地址
以此类推,我们把这种存储方式称为链式存储。
链表的基本操作,一般包括:
-
节点设计
-
初始化空链表
-
增删节点
-
链表遍历
-
销毁链表
单向链表
无头结点的单向链表
// 节点
struct node
{
int data; //数据域
// 指针域,指向下一个节点(存放下一个节点的地址)
struct node *next;
};
不带头结点的单向链表的相关操作:
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct Node
{
datatype data; // 数据域
struct Node *next; // 指针域
} node;
node *create_newnode(datatype data) // 创建新节点,返回新节点
{
node *pnew = malloc(sizeof(node)); // 将节点开在堆空间,节点类型实质上非指针,而是定义了一个结构体指针指向该节点,故返回该指针时,别的指针也能通过这个指针找到节点地址
pnew->data = data;
pnew->next = NULL;
return pnew;
}
node *create_list(void) // 创建链表,返回头结点
{
// 一开始链表为空
node *pnew = NULL;
node *first = NULL;
node *last = NULL;
while (1)
{
datatype data;
scanf("%d", &data);
if (data == 0)
break;
pnew = create_newnode(data);
if (pnew == NULL)
return NULL;
if (first == NULL && last == NULL) // 空链表
{
first = last = pnew;
}
else //(头插法)逆序
{
pnew->next = first;
first = pnew;
}
// else // (尾插法)正序
// {
// last->next = pnew;
// last = pnew; // 更新尾节点指针
// }
}
return first;
}
// 修改节点 为什么需要定义一个新的指针p来遍历链表呢?因为在修改过程中,我们需要遍历整个链表找到所有符合条件的节点,并修改它们的数据。直接使用first来遍历可能会导致遍历过程中改变first指针指向的节点,从而导致链表遍历的中断或错误修改。在这种情况下,如果first->data == olddata成立,会直接修改first指向的节点的数据。这可能会破坏原始链表的结构,导致无法正确遍历整个链表或者漏掉某些节点。
node *update(node *first, datatype olddata, datatype newdata)
{
node *p = first;
while (p)
{
if (p->data == olddata)
{
p->data = newdata;
}
p = p->next;
}
return first;
}
// 插入节点 同上修改节点的道理
node *insert_node(node *first, datatype olddata, datatype newdata)
{
node *p1 = first;
while (p1)
{
if (p1->data == olddata)
{
node *pnew = create_newnode(newdata);
pnew->next = p1->next;
p1->next = pnew;
}
p1 = p1->next;
}
return first;
}
// 删除节点
node *delete_node(node *first, datatype data)
{
if (first == NULL)
return NULL;
node *p3 = first;
node *pre = NULL; // 遍历指针的前一个位置,刚开始时,遍历指针在首节点,故前一个为空
while (p3)
{
if (p3->data == data)
{
break; // 找到要删除的元素了,则跳出循环
}
pre = p3;
p3 = p3->next;
} // 若未找到,则遍历完p3为空
if (p3) // 找到要删除的元素了
{
if (p3 == first) // 删除首节点
{
first = first->next;
}
else // 删除非首节点
pre->next = p3->next;
p3->next = NULL;
free(p3);
p3 = NULL;
}
return first;
}
// 删除多个节点
node *delete_allnode(node *first, datatype data)
{
if (first == NULL)
return NULL;
node *p3 = first;
node *pre = NULL; // 遍历指针的前一个位置,刚开始时,遍历指针在首节点,故前一个为空
while (p3)
{
while (p3)
{
if (p3->data == data)
{
break; // 找到要删除的元素了,则跳出循环
}
pre = p3;
p3 = p3->next;
} // 若未找到,则遍历完p3为空
if (p3) // 找到要删除的元素了
{
if (p3 == first) // 删除首节点
{
first = first->next; // 首节点变成后一个节点
p3->next = NULL;
free(p3);
p3 = first; // 依然从首节点开始
}
else // 删除非首节点
{
pre->next = p3->next;
p3->next = NULL;
free(p3);
p3 = pre->next; // 从下一个节点开始
}
}
}
return first;
}
// 打印链表 一般不将首节点指针作为边遍历指针
void printlist(node *first)
{
if (first == NULL)
return;
node *p2 = first;
while (p2)
{
printf("%d\t", p2->data);
p2 = p2->next;
}
printf("\n");
}
// 销毁链表
void destory_list(node *first)
{
node *p4 = first;
node *temp = NULL;
while (p4)
{
temp = p4->next;
p4->next = NULL;
free(p4);
p4 == temp;
}
}
int main(int argc, char const *argv[])
{
// 创建链表
node *first = create_list();
// 修改节点
first = update(first, 4, 44);
// 打印链表
printlist(first);
// 插入节点
first = insert_node(first, 44, 55);
// 打印链表
printlist(first);
// 删除节点
first = delete_allnode(first, 1);
// 打印链表
printlist(first);
// 销毁链表
destory_list(first);
return 0;
}
带头结点的单向链表
// 定义数据节点
struct node
{
dataType data; // 数据域
struct node *next; // 指针域,存放(指向)下一个节点的地址
};
//-----------------------------------
// 定义头节点
struct headNode
{
struct node *first; // 指向第一个数据节点
struct node *last; // 指向最后一个数据节点
int nodeNumber; // 记录链表节点数
};
相关操作代码:增删改查
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node // 数据节点类型
{
datatype data;
struct node *next;
} Node;
typedef struct head // 头结点类型
{
Node *first;
Node *last;
int datanumber; // 节点个数
} Head;
// 初始化头结点
Head *init_head(void)
{
Head *head = (Head *)malloc(sizeof(Head));
if (head == NULL)
return NULL;
head->datanumber = 0;
head->first = NULL;
head->last = NULL;
return head;
}
// 创建节点
Node *create_newnode(datatype data)
{
Node *pnew = malloc(sizeof(Node));
if (pnew == NULL)
{
perror("");
}
pnew->data = data;
pnew->next = NULL;
return pnew;
}
// 尾插法
void addtail(Head *head, Node *pnew)
{
{
head->last->next = pnew;
head->last = pnew;
}
}
// 头插法
void addhead(Head *head, Node *pnew)
{
pnew->next = head->first;
head->first = pnew;
}
// 打印链表
void printlist(Head *head)
{
if (head == NULL)
{
return;
}
Node *p = head->first;
while (p)
{
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
// 创建链表
Head *create_list(void)
{
// 初始化头结点
Head *head = init_head();
// 创建节点
printf("enter data:");
while (1)
{
int data = 0;
scanf("%d", &data);
if (data == 0)
break;
Node *pnew = create_newnode(data);
if (head->first == NULL && head->last == NULL) // 空链表
head->first = head->last = pnew;
else
addtail(head, pnew);
// addhead(head, pnew);
head->datanumber++;
}
return head;
}
// 更新数据结点
Head *update_node(Head *head, datatype old_data, datatype new_data)
{
if (head == NULL)
return NULL;
Node *p1 = head->first;
while (p1)
{
if (p1->data == old_data)
{
p1->data = new_data;
}
p1 = p1->next;
}
return head;
}
// 插入数据结点
Head *insert_node(Head *head, datatype old_data, datatype new_data)
{
if (head == NULL)
return NULL;
Node *pnew = create_newnode(new_data);
Node *p2 = head->first;
Node *pre = NULL;
while (p2)
{
if (p2->data == old_data)
{
break;
}
pre = p2;
p2 = p2->next;
}
if (p2)
{
// 在首节点
if (p2 == head->first)
{
pnew->next = head->first;
head->first = pnew;
}
// 非首节点
else
{
pre->next = pnew;
pnew->next = p2;
}
}
return head;
}
// 删除数据结点
Head *delete_node(Head *head, datatype old_data)
{
if (head == NULL)
return NULL;
Node *p3 = head->first;
Node *pre = NULL;
while (p3)
{
while (p3)
{
if (p3->data == old_data)
{
break;
}
pre = p3;
p3 = p3->next;
}
// 若为首节点
if (p3)
{
if (p3 == head->first)
{
head->first = p3->next;
p3->next = NULL;
free(p3);
head->datanumber--;
p3 = head->first;
}
// 若为非首节点
else
{
pre->next = p3->next;
p3->next = NULL;
free(p3);
head->datanumber--;
p3 = pre->next;
}
}
}
return head;
}
// 销毁链表
void destory(Head *head)
{
Node *temp = head->first;
while (temp)
{
head->first = head->first->next;
temp->next = NULL;
free(temp);
temp = head->first;
}
head->first = NULL;
head->last = NULL;
free(head);
}
int main()
{
// 创建链表
Head *head = create_list();
// 打印链表
printf("原始链表:");
printlist(head);
// 更新节点
int old_data, new_data;
printf("请输入原数据和要更新的数据:");
scanf("%d%d", &old_data, &new_data);
head = update_node(head, old_data, new_data);
printf("更新节点后的链表:");
printlist(head);
// 添加节点
printf("请输入原数据和要插入的数据:");
scanf("%d%d", &old_data, &new_data);
head = insert_node(head, old_data, new_data);
printf("添加节点后的链表:");
printlist(head);
// 删除节点
printf("请输入要删除的数据:");
scanf("%d", &old_data);
head = delete_node(head, old_data);
printf("删除节点后的链表:");
printlist(head);
// 销毁链表
destory(head);
return 0;
}
双向链表
// 创建数据节点
struct node
{
dataType data;
struct node *prev;// 指向上一个节点
struct node *next;// 指向下一个节点
};
// 创建头节点
struct headNode
{
struct node *first; // 指向首节点
struct node *last; // 指向最后一个节点
int nodeNumber; // 记录节点数
};
双向链表的增删改查,代码实现:
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node // 定义数据节点类型
{
datatype data;
struct node *next;
struct node *pre;
} node;
typedef struct head // 定义头节点类型
{
node *first;
node *last;
int datamount;
} Head;
// 初始化头结点
Head *init_head()
{
Head *head = malloc(sizeof(Head));
if (head == NULL)
return NULL;
head->first = NULL;
head->last = NULL;
head->datamount = 0;
return head;
}
//创建数据结点
node *create_node(datatype data)
{
node *pnew = malloc(sizeof(node));
if (pnew == NULL)
return NULL;
pnew->data = data;
pnew->next = NULL;
pnew->pre = NULL;
return pnew;
}
// 头插法
void addhead(Head *head, node *pnew)
{
pnew->next = head->first;
head->first->pre = pnew;
head->first = pnew;
}
// 尾插法
void addtail(Head *head, node *pnew)
{
pnew->pre = head->last;
head->last->next = pnew;
head->last = pnew;
}
// 打印链表
void printlist(Head *head)
{
node *p = head->first;
while (p)
{
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
//创建链表
Head *create_list()
{
// 初始化头结点
Head *head = init_head();
printf("enter data: ");
while (1)
{
datatype data = 0;
scanf("%d", &data);
if (data == 0)
break;
// 创建节点
node *pnew = create_node(data);
if (head->first == NULL && head->last == NULL) // 空链表
head->first = head->last = pnew;
else // 非空链表
// 头插法
// addhead(head, pnew);
// 尾插法
addtail(head, pnew);
head->datamount++;
}
return head;
}
//更新数据结点
Head *update_node(Head *head, datatype old_data, datatype new_data)
{
if (head == NULL)
return NULL;
node *p1 = head->first;
while (p1)
{
if (p1->data == old_data)
p1->data = new_data;
p1 = p1->next;
}
return head;
}
//插入数据结点
Head *insert_node(Head *head, datatype old_data, datatype new_data)
{
if (head == NULL)
return NULL;
node *p1 = head->first;
node *pre = NULL;
node *pnew = create_node(new_data);
while (p1)
{
if (p1->data == old_data)
break;
pre = p1;
p1 = p1->next;
}
if (p1)
{
// 若为头结点
if (p1 == head->first)
{
pnew->next = head->first;
head->first->pre = pnew;
pnew = head->first;
}
// 若为非头结点
else
{
pnew->next = p1;
p1->pre = pnew;
pnew->pre = pre;
pre->next = pnew;
}
head->datamount++;
}
return head;
}
//删除数据结点
Head *delete_node(Head *head, datatype old_data)
{
if (head == NULL)
return NULL;
node *p1 = head->first;
node *pre = NULL;
while (p1)
{
while (p1)
{
if (p1->data == old_data)
{
break;
}
pre = p1;
p1 = p1->next;
}
if (p1)
{
// 若为头结点
if (p1 == head->first)
{
head->first = p1->next;
p1->next = NULL;
p1->pre = NULL;
free(p1);
head->datamount--;
p1 = head->first;
}
// 若为尾结点
else if (p1 == head->last)
{
head->last = p1->pre;
pre->next = NULL;
p1->next = NULL;
p1->pre = NULL;
free(p1);
head->datamount--;
p1 = NULL;
}
// 若为中间节点
else
{
pre->next = p1->next;
p1->next->pre = pre;
p1->next = NULL;
p1->pre = NULL;
free(p1);
head->datamount--;
p1 = pre->next;
}
}
}
return head;
}
//销毁链表
void destroy(Head *head)
{
if (head == NULL)
{
printf("NUlllist\n");
return;
}
node *p = head->first;
while (p)
{
head->first = p->next;
p->next = NULL;
p->pre = NULL;
free(p);
p = head->first;
}
head->first = NULL;
head->last = NULL;
free(head);
}
int main(int argc, char const *argv[])
{
// 创建链表
Head *head = create_list();
// 打印链表
printf("原始链表:");
printlist(head);
// 更新节点
int old_data, new_data;
printf("请输入原数据和要更新的数据:");
scanf("%d%d", &old_data, &new_data);
head = update_node(head, old_data, new_data);
printf("更新节点后的链表:");
printlist(head);
// 添加节点
printf("请输入原数据和要插入的数据:");
scanf("%d%d", &old_data, &new_data);
head = insert_node(head, old_data, new_data);
printf("添加节点后的链表:");
printlist(head);
// 删除节点
printf("请输入要删除的数据:");
scanf("%d", &old_data);
head = delete_node(head, old_data);
printf("删除节点后的链表:");
printlist(head);
// 销毁链表
destroy(head);
return 0;
}
双向循环链表
双向循环链表的增删改查,代码实现:
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node // 定义数据节点类型
{
datatype data;
struct node *next;
struct node *pre;
} node;
typedef struct head // 定义头节点类型
{
node *first;
node *last;
int datamount;
} Head;
// 初始化头结点
Head *init_head()
{
Head *head = malloc(sizeof(Head));
if (head == NULL)
return NULL;
head->first = NULL;
head->last = NULL;
head->datamount = 0;
return head;
}
//创建结点
node *create_node(datatype data)
{
node *pnew = malloc(sizeof(node));
if (pnew == NULL)
return NULL;
pnew->data = data;
pnew->next = NULL;
pnew->pre = NULL;
return pnew;
}
// 头插法
void addhead(Head *head, node *pnew)
{
pnew->next = head->first;
head->first->pre = pnew;
head->first = pnew;
}
// 尾插法
void addtail(Head *head, node *pnew)
{
pnew->pre = head->last;
head->last->next = pnew;
head->last = pnew;
}
// 打印链表
void printlist(Head *head)
{
node *p = head->first;
for (int i = 0; i < head->datamount; i++)
{
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
//创建链表
Head *create_list()
{
// 初始化头结点
Head *head = init_head();
printf("enter data: ");
while (1)
{
datatype data = 0;
scanf("%d", &data);
if (data == 0)
break;
// 创建节点
node *pnew = create_node(data);
if (head->first == NULL && head->last == NULL) // 空链表
head->first = head->last = pnew;
else // 非空链表
// 头插法
// addhead(head, pnew);
// 尾插法
addtail(head, pnew);
head->datamount++;
}
// 设计链表为循环属性
if (head->datamount != 0)
{
head->first->pre = head->last;
head->last->next = head->first;
}
return head;
}
//更新数据结点
Head *update_node(Head *head, datatype old_data, datatype new_data)
{
if (head == NULL)
return NULL;
node *p1 = head->first;
for (int i = 0; i < head->datamount; i++)
{
if (p1->data == old_data)
p1->data = new_data;
p1 = p1->next;
}
return head;
}
//插入数据结点
Head *insert_node(Head *head, datatype old_data, datatype new_data)
{
if (head == NULL)
return NULL;
node *p1 = head->first;
node *pre = NULL;
node *pnew = create_node(new_data);
for (int i = 0; i < head->datamount; i++)
{
if (p1->data == old_data)
break;
pre = p1;
p1 = p1->next;
}
if (p1)
{
// 若为头结点
if (p1 == head->first)
{
pnew->next = head->first;
head->first->pre = pnew;
pnew = head->first;
}
// 若为非头结点
else
{
pnew->next = p1;
p1->pre = pnew;
pnew->pre = pre;
pre->next = pnew;
}
head->datamount++;
}
return head;
}
//删除数据结点
Head *delete_node(Head *head, datatype old_data) {
if (head == NULL || head->datamount == 0) {
return head;
}
node *p1 = head->first;
node *pre = NULL;
int n = head->datamount;
while (n > 0) {
int inner_n = n;
while (inner_n > 0) {
if (p1->data == old_data) {
if (p1 == head->first) {
head->first = p1->next;
}
if (p1 == head->last) {
head->last = p1->pre;
}
//为非首节点
if (pre != NULL) {
pre->next = p1->next;
}
//为非尾节点
if (p1->next != NULL) {
p1->next->pre = pre;
}
node *temp = p1;
p1 = p1->next;
free(temp);
head->datamount--;
break;
}
//若没找到,遍历次数也少1,往后移
pre = p1;
p1 = p1->next;
inner_n--;
}
n--;
}
return head;
}
//销毁链表
void destroy(Head *head)
{
if (head == NULL)
{
printf("NUlllist\n");
return;
}
node *p = head->first;
while (p)
{
head->first = p->next;
p->next = NULL;
p->pre = NULL;
free(p);
p = head->first;
}
head->first = NULL;
head->last = NULL;
free(head);
}
int main(int argc, char const *argv[])
{
// 创建链表
Head *head = create_list();
// 打印链表
printf("原始链表:");
printlist(head);
// 更新节点
int old_data, new_data;
printf("请输入原数据和要更新的数据:");
scanf("%d%d", &old_data, &new_data);
head = update_node(head, old_data, new_data);
printf("更新节点后的链表:");
printlist(head);
// 添加节点
printf("请输入原数据和要插入的数据:");
scanf("%d%d", &old_data, &new_data);
head = insert_node(head, old_data, new_data);
printf("添加节点后的链表:");
printlist(head);
// 删除节点
printf("请输入要删除的数据:");
scanf("%d", &old_data);
head = delete_node(head, old_data);
printf("删除节点后的链表:");
printlist(head);
// // 销毁链表
// destroy(head);
return 0;
}
双向链表和循环链表的练习题
2.判断链表是否有环
// 2.判断链表是否有环
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int datatype;
typedef struct node // 定义数据节点类型
{
datatype data;
struct node *next;
struct node *pre;
} node;
typedef struct head // 定义头节点类型
{
node *first;
node *last;
int datamount;
} Head;
// 初始化头结点
Head *init_head()
{
Head *head = malloc(sizeof(Head));
if (head == NULL)
return NULL;
head->first = NULL;
head->last = NULL;
head->datamount = 0;
return head;
}
node *create_node(datatype data)
{
node *pnew = malloc(sizeof(node));
if (pnew == NULL)
return NULL;
pnew->data = data;
pnew->next = NULL;
pnew->pre = NULL;
return pnew;
}
// 头插法
void addhead(Head *head, node *pnew)
{
pnew->next = head->first;
head->first->pre = pnew;
head->first = pnew;
}
// 尾插法
void addtail(Head *head, node *pnew)
{
pnew->pre = head->last;
head->last->next = pnew;
head->last = pnew;
}
// 打印链表
void printlist(Head *head)
{
node *p = head->first;
for (int i = 0; i < head->datamount; i++)
{
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
//
Head *create_list()
{
// 初始化头结点
Head *head = init_head();
printf("enter data: ");
while (1)
{
datatype data = 0;
scanf("%d", &data);
if (data == 0)
break;
// 创建节点
node *pnew = create_node(data);
if (head->first == NULL && head->last == NULL) // 空链表
head->first = head->last = pnew;
else // 非空链表
// 头插法
// addhead(head, pnew);
// 尾插法
addtail(head, pnew);
head->datamount++;
}
// 设计链表为循环属性
if (head->datamount != 0)
{
head->first->pre = head->last;
head->last->next = head->first;
}
return head;
}
// 判断链表是否有环
bool Islooplist(Head *head)
{
node *fast = head->first;
node *slow = head->first;
while (fast && slow)
{
fast = fast->next->next; // fast走两步
slow = slow->next; // slow走一步
if (fast == slow)
return true;
}
return false;
}
int main(int argc, char const *argv[])
{
// 创建链表
Head *head = create_list();
// 打印链表
printf("原始链表:");
printlist(head);
// 2.判断链表是否有环
printf("%d\n",Islooplist(head));
return 0;
}
合并两个链表,按从小到大排序(拆链表,比较节点,插入节点到新链表)
// 合并两个链表,按从小到大排序(拆链表,比较节点,插入节点到新链表)
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
} node;
typedef struct head
{
node *first;
node *last;
int nodenumber;
} listhead;
// 初始化头结点
listhead *init_headlist()
{
listhead *head = malloc(sizeof(listhead));
if (head == NULL)
return NULL;
head->first = NULL;
head->last = NULL;
head->nodenumber = 0;
return head;
}
// 创建结点
node *create_node(datatype data)
{
node *pnew = malloc(sizeof(node));
if (pnew == NULL)
return NULL;
pnew->data = data;
pnew->next = NULL;
return pnew;
}
// 尾插法
void addtail(listhead *head, node *pnew)
{
if (head == NULL || pnew == NULL)
return;
if (head->last == NULL)
{
head->first = head->last = pnew;
}
else
{
head->last->next = pnew;
head->last = pnew;
}
}
// 创建链表
listhead *create_list()
{
// 初始化头结点
listhead *head = init_headlist();
// 创建结点
printf("please enter data: ");
while (1)
{
int data = 0;
scanf("%d", &data);
if (data == 0)
break;
node *pnew = create_node(data);
// 空链表
if (head->first == NULL && head->last == NULL)
head->first = head->last = pnew;
else
addtail(head, pnew);
head->nodenumber++;
}
return head;
}
void print_list(listhead *head)
{
if (head == NULL)
return;
node *p = head->first;
while (p)
{
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
// 合并两条链表
listhead *mergetwolists(listhead *head1, listhead *head2)
{
if (head1 == NULL)
return head2;
if (head2 == NULL)
return head1;
// 创建合并之后的空链表
// 初始化头结点
listhead *Head = init_headlist();
node *temp1 = head1->first;
node *temp2 = head2->first;
// 遍历两条链表
while (temp1 != NULL && temp2 != NULL)
{
if (temp1->data < temp2->data)
{
node *pnew = create_node(temp1->data);
addtail(Head, pnew);
Head->nodenumber++;
temp1 = temp1->next;
}
else
{
node *pnew = create_node(temp2->data);
addtail(Head, pnew);
Head->nodenumber++;
temp2 = temp2->next;
}
}
// 剩余结点
while (temp1 != NULL)
{
node *pnew = create_node(temp1->data);
addtail(Head, pnew);
Head->nodenumber++;
temp1 = temp1->next;
}
while (temp2 != NULL)
{
node *pnew = create_node(temp2->data);
addtail(Head, pnew);
Head->nodenumber++;
temp2 = temp2->next;
}
return Head;
}
void destroy(listhead *head)
{
if (head == NULL)
{
printf("NUlllist\n");
return;
}
node *p = head->first;
while (p)
{
head->first = p->next;
p->next = NULL;
free(p);
p = head->first;
}
head->first = NULL;
head->last = NULL;
free(head);
}
int main(int argc, char const *argv[])
{
// 创建第一条链表
listhead *head1 = create_list();
printf("第一条链表为:");
print_list(head1);
// 创建第二条链表
listhead *head2 = create_list();
printf("第二条链表为:");
print_list(head2);
// 合并两条链表
listhead *head3 = mergetwolists(head1, head2);
printf("合并后的链表为:");
print_list(head3);
// 销毁链表
destroy(head1);
destroy(head2);
destroy(head3);
return 0;
}
栈
1.基本概念
栈是一种逻辑结构,是特殊的线性表,特殊在只能在固定一端操作
只要满足上述条件,那么这种特殊的线性表就会呈现出一种"后进先出"的逻辑,这种逻辑就被称为栈,栈在生活中到处可见,比如堆叠的盘子、电梯中的人等等。
顺序栈
顺序栈的练习:
使用顺序栈,接收键盘的输入,实现如下功能:
-
输入数字时,依次入栈。
-
输入字母时,依次出栈。
-
每次入栈或者出栈,都将顺序栈中的各个元素输出出来。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
// 顺序栈结构体
typedef struct stack
{
datatype *data; // 指向顺序链表的首地址
int size; // 栈的容量
int top; // 栈顶元素下标
} Stack;
// 初始化栈
Stack *init_stack(int size)
{
Stack *stack = malloc(sizeof(Stack));
if (stack == NULL)
return NULL;
stack->data = malloc(size * sizeof(datatype));
if (stack->data == NULL)
return NULL;
stack->size = size;
stack->top = -1;
}
// 判断是否栈满
bool Isfull(Stack *stack)
{
return stack->size - 1 == stack->top;
}
// 判断栈是否空
bool Isempty(Stack *stack)
{
return stack->top == -1;
}
// 入栈
bool push_stack(Stack *stack, datatype data)
{
if (Isfull(stack))
return false;
stack->data[++stack->top] = data;
return true;
}
// 取栈顶元素
bool top(Stack *stack, datatype *data) // 传地址使得调用该函数的函数可以拿到栈顶的值
{
if (Isempty(stack))
return false;
*data = stack->data[stack->top];
return true;
}
// 出栈
bool pop(Stack *stack, datatype *data)
{
if (!top(stack, data))
return false;
stack->top--;
return true;
}
// 打印入栈时的栈
void printstack(Stack *stack)
{
for (int i = 0; i <= stack->top; i++)
{
printf("%d\t", stack->data[i]);
}
printf("\n");
}
// 打印出栈时的栈
void printstack1(Stack *stack)
{
for (int i = stack->top; i >= 0; i--)
{
printf("%d\t", stack->data[i]);
}
printf("\n");
}
int main()
{
// 初始化栈
int size = 0;
printf("enter stack size:");
scanf("%d", &size);
Stack *stack = init_stack(size);
printf("enter data:");
while (1)
{
datatype data;
int ret = scanf("%d", &data);
if (ret)
{
if (!push_stack(stack, data))
{
printf("栈已满,无法入栈");
continue;
}
printstack(stack);
}
else
{
// 清空输入缓冲区
while (getchar() != '\n');
if (!pop(stack, &data))
{
printf("栈为空,无法出栈");
continue;
}
printstack1(stack);
}
}
printf("\n");
return 0;
}
链式栈
链式栈的练习:
使用链式栈,实现十进制转八进制:键盘输入一个十进制数,经过链式栈的相关算法,输出八进制数。
// 使用链式栈,实现十进制转八进制:键盘输入一个十进制数,经过链式栈的相关算法,输出八进制数。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
} node;
typedef struct linkstack
{
node *top; // 栈顶指针
int size; // 记录链式栈中的元素个数
} linkstack;
// 初始化空栈
linkstack *init_linkstack()
{
linkstack *stack = malloc(sizeof(linkstack));
if (stack == NULL)
return NULL;
stack->size = 0;
stack->top = NULL;
return stack;
}
// 判断栈是否为空
bool Isempty(linkstack *stack)
{
if (stack == NULL)
return false;
return stack->size == 0;
}
// 入栈
bool push_stack(linkstack *stack, datatype data)
{
// 创建节点,永远头插法插入
node *pnew = malloc(sizeof(node));
if (pnew == NULL)
return false;
pnew->data = data;
// 将节点置于栈顶
pnew->next = stack->top;
stack->top = pnew;
stack->size++;
return true;
}
// 取栈顶元素
bool top_stack(linkstack *stack, datatype *data)
{
if (Isempty(stack))
return false;
*data = stack->top->data;
return true;
}
// 出栈
bool pop_stack(linkstack *stack, datatype *data)
{
if (Isempty(stack))
return false;
node *temp = stack->top;
// 将栈顶元素剔除
stack->top = temp->next;
temp->next = NULL;
// 返回栈顶元素,并释放节点
*data = temp->data;
free(temp);
return true;
}
// 打印链式栈
void print_linkstack(linkstack *stack)
{
for (node *temp = stack->top; temp != NULL; temp = temp->next)
{
printf("%d", temp->data);
}
printf("\n");
}
int main()
{
// 初始化空栈
linkstack *stack = init_linkstack();
int data;
printf("enter data:");
scanf("%d", &data);
while (1)
{
int remainder = data % 8;
push_stack(stack, remainder);
data = data / 8;
if (data == 0)
break;
}
printf("转换后的八进制数为:");
print_linkstack(stack);
return 0;
}
队列
1. 基本概念
队列是最常见的概念,日常生活经常需要排队,仔细观察队列会发现,队列是一种逻辑结构,是一种特殊的线性表。特殊在:
-
只能在固定的两端操作线性表
只要满足上述条件,那么这种特殊的线性表就会呈现一种“先进先出”的逻辑,这种逻辑就被称为队列。
循环队列
与其他的逻辑结构类似,队列可以采用顺序存储形成循环队列,也可以采用链式存储形成链式队列。顺序存储的队列之所以被称为循环队列,是因为可以利用更新队头队尾的下标信息,来循环地利用整个数组,出队入队时也不必移动当中的数据。
满队和空队的约定如下:
-
当front与rear相等时,队列为空
-
当(rear+1)%队列的总容量 与front相等时,队列为满
-
注意: 循环队列中,需要牺牲一个存储位置来区分空队和满队
循环队列的练习:
构建一个顺序存储的循环队列,当用户输入数字时,将数字入队,当用户输入字母时,将队头元素出队。每次操作队列之后,将队列中的元素显示出来。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
typedef struct queue
{
datatype *data;//存储队列中的元素
int front; // 队头下标
int rear; // 队尾下标
int capacity; // 队的容量
} queue;
// 初始化队列
queue *init_queue(int capacity)
{
queue *q = malloc(sizeof(queue));
if (q == NULL)
return NULL;
q->capacity = capacity;
q->data = malloc(capacity * sizeof(datatype));
q->front = 0;
q->rear = 0;
return q;
}
// 判断是否队空
bool Isempty(queue *q)
{
if (q == NULL)
return false;
return q->front == q->rear;
}
// 判断是否队满
bool Isfull(queue *q)
{
if (q == NULL)
return false;
return (q->rear + 1) % q->capacity == q->front;
}
// 入队
bool push_queue(queue *q, datatype data)
{
if (Isfull(q))
{
printf("队满,无法入队\n");
return false;
}
q->data[q->rear] = data;
q->rear = (q->rear + 1) % q->capacity;
return true;
}
// 出队
bool pop_queue(queue *q, datatype *data)
{
if (Isempty(q))
{
printf("队空,无元素\n");
return false;
}
*data = q->data[q->front];
q->front = (q->front + 1) % q->capacity;
return true;
}
// 打印队列中的元素
void print_queue(queue *q)
{
if (q == NULL || Isempty(q))
{
printf("队空,无元素\n");
return;
}
//求得队头到队尾的元素个数,加上容量确保被除数是正的,q->rear:队列的尾部指针,指向最后一个入队元素的下一个位置(即下一个待入队的位置)。
// int count = (q->rear - q->front + q->capacity) % q->capacity;
// int index = q->front;
// for (int i = 0; i < count; i++)
// {
// printf("%d\t", q->data[index]);
// index = (index + 1) % q->capacity;
// }
for(int i=q->front;i!=q->rear;i=(i+1)%q->capacity)
{
printf("%d\t", q->data[i]);
}
printf("\n");
}
int main()
{
// 初始化队列
queue *q = init_queue(6);
// 入队,出队
printf("enter data:");
while (1)
{
datatype data;
int ret = scanf("%d", &data);
if (ret == 1)
{
if (!push_queue(q, data))
{
printf("无法入队\n");
continue;
}
}
else
{
// 清空输入缓冲区
while (getchar() != '\n');
if (!pop_queue(q, &data))
{
printf("无法出队\n");
continue;
}
}
print_queue(q);
}
return 0;
}
链式队列
链式队列的组织形式与链表无异,只不过插入删除被约束在固定的两端。为了便于操作,通常也会创建所谓管理结构体,用来存储队头指针、队尾指针、队列元素个数等信息:
链式队列练习:
构建一个链式队列,当用户输入数字时,将数字入队,当用户输入字母时,将队头元素出队。每次操作队列之后,将队列中的元素显示出来。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int datatype;
// 链式队列节点
typedef struct node
{
datatype data;
struct node *next;
} node;
// 链式队列管理结构体
typedef struct queue
{
node *front;//队头指针
node *rear;//队尾指针
int size;//当前队列元素个数
} linkqueue;
// 初始化链式队列
linkqueue *init_queue()
{
linkqueue *q = malloc(sizeof(linkqueue));
if (q == NULL)
return NULL;
q->front = q->rear = NULL;
q->size = 0;
return q;
}
// 判断队列是否为空
bool Isempty(linkqueue *q)
{
return q->size == 0;
}
// 入队
bool push(linkqueue *q, datatype data)
{
node *pnew = malloc(sizeof(node));
if (pnew == NULL)
return false;
pnew->data = data;
pnew->next = NULL;
if (Isempty(q))
q->front = q->rear = pnew;
else
{
q->rear->next = pnew;
q->rear = pnew;
}
q->size++;
return true;
}
// 出队
bool pop(linkqueue *q, datatype *data)
{
if (Isempty(q))
return false;
*data = q->front->data;
if (q->size == 1)
{
q->front == NULL;
q->rear = NULL;
free(q->front);
}
else
{
node *temp = q->front;
q->front = q->front->next;
temp->next = NULL;
free(temp);
}
q->size--;
return true;
}
// 打印队列
void print_queue(linkqueue *q)
{
if (q == NULL || Isempty(q))
{
printf("队空,无法打印\n");
return;
}
for (node *temp = q->front; temp!=NULL; temp = temp->next)
{
printf("%d\t", temp->data);
}
printf("\n");
}
int main()
{
// 初始化队列
linkqueue *q = init_queue();
// 入队出队
printf("enter data:");
while (1)
{
datatype data;
int ret = scanf("%d", &data);
if (ret)
{
if (!push(q, data))
{
printf("无法入队\n");
continue;
}
}
else
{
while (getchar() != '\n')
;
if (!pop(q, &data))
{
printf("无法出栈\n");
continue;
}
}
print_queue(q);
}
return 0;
}