01.数据结构画图
02.
//11.按值查找返回位置
int search_value(node_p H,int value)
{
if(H==NULL){
printf("入参为空.\n");
return -1;
}
if(empty_double(H)){
return -2;
}
int i;
node_p p=H->next;
for(i=0;p!=NULL;i++,p=p->next)
{
if(p->data==value){
return i;
}
}
}
//12.按位置修改元素
void update_pos(node_p H,int pos,int new_value)
{
if(H==NULL){
printf("入参为空.\n");
return;
}
if(empty_double(H)){
printf("双项链表为空,没有值可以修改.\n");
return;
}
if(pos<0){
return;
}
int i;
node_p p;
for(i=0,p=H->next;i<pos;i++,p=p->next){
p->data=new_value;
}
}
02.自己实现双向循环链表的代码
a.创建
b.创建结点
c.头插
d.尾插
e.按位置插入
f.头删、尾删、按位置删除
g.按位置查找返回值
]main.c
#include "dloop.h"
int main()
{
node_p H=(node_p)create_dloop();
//1.头插
printf("请输出头插的第一个数字双向链表:\n");
insert_head(H,4);
show(H);
printf("请输出头插的第二个数字双向链表:\n");
insert_head(H,3);
show(H);
printf("请输出头插的第三个数字双向链表:\n");
insert_head(H,2);
show(H);
printf("请输出头插的第四个数字双向链表:\n");
insert_head(H,1);
show(H);
//2.尾插
printf("请输出尾插的第一个数字双向链表:\n");
insert_tail(H,5);
show(H);
printf("请输出尾插的第二个数字双向链表:\n");
insert_tail(H,6);
show(H);
//3.按位置插入
printf("请输出按位置插入双向链表:\n");
insert_pos(H,3,77);
show(H);
//4.头删
printf("请输出头删双向链表:\n");
dele_head(H);
show(H);
//5.尾删
printf("请输出头删双向链表:\n");
dele_tail(H);
show(H);
//6.按位置删除
printf("请输出按位置删除双向链表:\n");
delete_pos(H,3);
show(H);
//7.按位置查找返回值
int ret=search_value(H,2);
printf("请输出按位查找的返回值%d:\n",ret);
show(H);
printf("销毁前:\n");
printf("%p\n",H);
free_loop(&H);
printf("销毁后:\n");
printf("%p\n",H);
}
dloop.c
#include "dloop.h"
//1.创建双链表的空结点
node_p create_dloop()
{
node_p H=(node_p)malloc(sizeof(node));
if(H==NULL)
{
printf("申请内存失败.\n");
return NULL;
}
H->pri=H;
H->next=H;
H->len=0;
return H;
}
//2.申请结点
node_p create_node(int value)
{
node_p new=(node_p)malloc(sizeof(node));
new->next=NULL;
new->data=value;
}
//3.判空
int empty_dloop(node_p H)
{
if(H==NULL){
printf("入参为空.\n");
return -1;
}
return H->next==H?1:0;
}
//4.头插
void insert_head(node_p H,int value)
{
if(H==NULL)
{
printf("入参为空.\n");
return;
}
node_p new=create_node(value);
new->next=H->next;
new->pri=H;
if(H->next!=NULL){
H->next->pri=new;
}
H->next=new;
H->len++;
}
//5.尾插
void insert_tail(node_p H,int value)
{
if(H==NULL)
{
printf("入参为空.\n");
return;
}
int i;
node_p new=create_node(value);
node_p p=H;
//1.找到了最后一个结点的位置
while(p->next!=H){
p=p->next;
}
new->pri=p;
new->next=H;
p->next=new;
H->pri=new;
H->len++;
}
//6.按位置插入
void insert_pos(node_p H,int pos,int value)
{
if(H==NULL){
printf("入参为空.\n");
return;
}
if(pos<0){
printf("插入位置的数据不合理.\n");
return;
}
int i;
node_p p;
for(i=1,p=H->next;i<pos-1;i++,p=p->next)
{
if(p==H){
printf("位置不合理.\n");
return;
}
}
if(p==H){
printf("位置不合理.\n");
return;
}
node_p new=(node_p)create_node(value);
new->next=p->next;
new->pri=p;
if(p->next!=H){
p->next->pri=new;
}
p->next=new;
H->len++;
}
//7.输出
void show(node_p H)
{
if(H==NULL){
printf("入参为空.\n");
return;
}
if(empty_dloop(H)){
printf("双向循环链表为空,无输出.\n");
return;
}
node_p p=H->next;
while(p!=H){
printf("%d->",p->data);
p=p->next;
}
printf("NULL\n");
}
//8.头删
void dele_head(node_p H)
{
if(H==NULL){
printf("入参为空.\n");
return;
}
if(empty_dloop(H)){
printf("循环双链表为空,无数据可删除.\n");
return;
}
node_p dele=H->next;
//判断是否就一个数据
if(dele->next!=H){
dele->next->pri=H;
}
H->next=dele->next;
free(dele);
H->len--;
}
//9.尾删
void dele_tail(node_p H)
{
if(H==NULL)
{
printf("入参为空.\n");
return;
}
node_p p=H;
while(p->next!=H){
p=p->next;
}
p->pri->next=H;
H->pri=p->pri;
free(p);
H->len--;
}
//10.按位置删除
void delete_pos(node_p H,int pos)
{
if(H==NULL){
printf("入参为空.\n");
return;
}
if(empty_dloop(H)){
printf("双向链表为空.\n");
return;
}
node_p p;
int i;
for(i=1,p=H->next;i<pos;i++,p=p->next){
if(p==H){
printf("位置不合理.\n");
return;
}
}
if(p==H){
printf("位置不合理.\n");
return;
}
//1.pos+1结点的前驱指向pos-1结点
if(p->next!=H){
p->next->pri=p->pri;
}
//2.pos-1结点的后继指向pos+1结点
p->pri->next=p->next;
free(p);
H->len--;
}
//11.按位置查找返回值
int search_value(node_p H,int pos)
{
if(H==NULL)
{
printf("入参为空.\n");
return -1;
}
if(empty_dloop(H)){
printf("双向循环链表为空.\n");
return -2;
}
int i;
node_p p;
for(i=1,p=H->next;i<pos;i++,p=p->next){
if(p==H){
printf("位置不合理.\n");
return -3;
}
}
if(p==H){
printf("位置不合理.\n");
return -3;
}
return p->data;
}
//12.释放链表
void free_loop(node_p *H)
{
if(H==NULL||*H==NULL)
{
printf("入参为空.\n");
return;
}
while((*H)->next!=(*H)){
dele_head(*H);
}
free(*H);
*H==NULL;
}
dlloop.h
#ifndef __DLOOP_H__
#define __DLOOP_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct node
{
union{
int len;
int data;
};
struct node *pri;
struct node *next;
}node,*node_p;
node_p create_dloop();
node_p create_node(int value);
int empty_dloop(node_p H);
void insert_head(node_p H,int value);
void insert_tail(node_p H,int value);
void insert_pos(node_p H,int pos,int value);
void show(node_p H);
void dele_head(node_p H);
void dele_tail(node_p H);
void delete_pos(node_p H,int pos);
int search_value(node_p H,int pos);
void free_loop(node_p *H);
#endif
03.整理链表和顺序表的优缺点
链表和顺序表是两种常见的数据存储结构,它们各自有不同的优缺点,下面为你详细介绍:
顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般采用数组来实现。
优点
- 随机访问效率高:通过数组下标可以直接访问任意位置的元素,时间复杂度为 $O(1)$。例如,在一个长度为 $n$ 的数组
arr
中,访问arr[i]
能快速定位到对应元素。- 缓存局部性好:由于顺序表的元素在内存中是连续存储的,计算机在访问元素时,会把相邻的数据也加载到缓存中,后续访问相邻元素时能更快获取,提高了访问效率。
- 空间利用率高:顺序表不需要额外的指针来维护元素之间的关系,只存储数据本身,因此空间利用率相对较高。
缺点
- 插入和删除操作效率低:在顺序表中间或开头插入、删除元素时,需要移动大量后续元素,平均时间复杂度为 $O(n)$。例如,在数组中间插入一个元素,需要将插入位置之后的所有元素依次向后移动一位。
- 空间扩展不便:当顺序表需要扩容时,通常需要重新分配一块更大的内存空间,并将原有的数据复制过去,操作比较耗时。而且如果一次性分配过大的空间,可能会造成内存浪费;分配过小,又需要频繁扩容。
- 内存分配要求高:顺序表要求内存空间必须连续,当需要存储大量数据时,可能难以找到足够大的连续内存空间。
链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。常见的链表有单链表、双向链表和循环链表。
优点
- 插入和删除操作效率高:在链表中插入或删除元素,只需要修改相邻节点的指针,不需要移动大量元素,时间复杂度为 $O(1)$(前提是已经找到插入或删除的位置)。例如,在单链表中插入一个新节点,只需修改前一个节点的指针和新节点的指针。
- 空间动态分配:链表的节点是在需要时动态分配内存的,不需要预先分配大量连续的内存空间,避免了内存浪费和分配失败的问题。
- 内存利用率高:链表不需要连续的内存空间,只要有足够的零散内存,就可以创建链表节点,适合存储大量数据。
缺点
- 随机访问效率低:链表只能从表头开始依次遍历查找元素,访问第 $i$ 个元素的平均时间复杂度为 $O(n)$,无法像顺序表那样直接通过下标访问。
- 额外空间开销大:链表的每个节点除了存储数据本身,还需要额外的指针来指向下一个节点(双向链表还需要指向前驱节点),增加了空间开销。
- 缓存局部性差:由于链表的节点在内存中是分散存储的,访问元素时可能无法充分利用缓存,导致访问效率降低。
综上所述,顺序表适合随机访问频繁、数据量固定的场景;链表适合插入和删除操作频繁、数据量动态变化的场景。