今天也要努力学习,争取考上杭电。
链表的知识
链表即线性表的链式存储。
通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据间的线性关系,对每个链表结点,除了存放数据元素的信息外,还需要存放一个指向其后继的指针(双链表则还需要一个指向前驱的指针)。
链表解决了顺序表需要大量连续存储单元的缺点,但链表要附加指针域,也浪费了存储空间。存储密度不如顺序表大。
相比顺序表的随机访问,链表则是非随机存取,需要从头挨个遍历,复杂度为O(n),但删除增加结点方便,每个结点的插入时间复杂度为O(1),但查找到操作位置需要的时间开销为O(n)。
单链表在找到某一结点时,访问后继结点复杂度为O(1),但访问前驱结点为O(n),所以为克服这个缺点引入了双链表,即多了一个前驱指针。
其中循环双链表,即头结点的前驱为表的最后一个结点,最后一个结点的后继为头结点。
本篇是用C语言实现的带头结点的循环双链表。
C代码实现
结构体定义:
//双链表结点结构定义
typedef struct DNode{
struct DNode *prior; //前驱
ElemType data; //数据
struct DNode *next; //后继
}DNode,*DNP;
相关函数代码:`
头文件、用到的宏定义:
#include<stdio.h>
#include<malloc.h>
#include<string.h>
//_bool类型
#define _bool int
#define true 1
#define false 0
//循环双链表元素数据类型 ElemType
#define ElemType int
初始化头结点、头插、尾插、按位插:
注:很多书上是头(尾)插法建立链表,就是说初始化表头和一开始建立链表插入数据放在一个功能实现了,但是我把它分开写的,把初始化单独写了一个函数,这样后面可以随时头插尾插。
//循环双链表初始化头结点
_bool IniHeadNode(DNP h){
h->prior=h; //初始前驱是尾结点,即头结点
h->next=h; //初始后继为头结点
h->data=0; //头结点data用来存放长度
return true;
}
//头插入链表 inilen:要插入数据的数量
_bool HeadInsert(DNP h,int inilen){
ElemType data;
DNP p=h;
while(inilen--){
DNP n=NULL;
//void* malloc(unsigned int size);
n=(DNP)malloc(sizeof(DNode));
if(n==NULL){
printf("Insufficient space\n");
return false;
}
scanf("%d",&data);
n->data=data;
n->next=p->next;
p->next->prior=n;
p->next=n;
n->prior=p;
h->data++;
}
return true;
}
//尾插入链表 inilen:要插入数据的数量
_bool TailInsert(DNP h,int inilen){
ElemType data;
DNP p=h;
while(inilen--){
DNP n=NULL;
//void* malloc(unsigned int size);
n=(DNP)malloc(sizeof(DNode));
if(n==NULL){
printf("Insufficient space\n");
return false;
}
scanf("%d",&data);
n->data=data;
n->next=p;
p->prior->next=n;
n->prior=p->prior;
p->prior=n;
h->data++;
}
return true;
}
//某一位置后插入一个新结点
_bool Insert(DNP h,int destin){
ElemType data;
DNP n=NULL,p=h;
n=(DNP)malloc(sizeof(DNode));
if(n==NULL){
printf("Insufficient space\n");
return false;
}
scanf("%d",&data);
while(destin--) p=p->next;
p->next->prior=n;
n->next=p->next;
n->prior=p;
p->next=n;
n->data=data;
return true;
}
遍历:
//正向遍历双链表 返回表长度
//反向同理,将后继改为前驱即可
int TraversalList(DNP h){
DNP p=h->next;
if(p==h){
printf("NULL\n");
return false;
}
while(p!=h){ //循环链表,最后一个结点的后继为头结点,不循环则为NULL
printf("%5d",p->data);
p=p->next;
}printf("\n");
return h->data;
}
删除、查询:
这里也偷懒了…因为感觉有很多好像都是一样的,像按值删除,我要先找出那个结点然后删除。所以直接写了查询和删除函数,后面另一个函数调用了。出现标记耦合,不太好,但是就这样吧…
//删除结点p
_bool DeleteNode(DNP p){
if(p==NULL) return false;
p->next->prior=p->prior;
p->prior->next=p->next;
free(p);
return true;
}
//按位查询结点 成功则返回查询到的结点地址
DNP SelectByLocation(DNP h,int destin){
if(destin>h->data){
printf("Location(%d) not present!\n",destin);
return NULL;
}
DNP p=h;
int flag=destin;
while(flag--){
p=p->next;
}
printf("Position %d is %d\n",destin,p->data);
return p;
}
//按值查询结点 成功则返回查询到的结点地址
DNP SelectByValue(DNP h,ElemType value){
DNP p=h->next;
int i=1;
while(p!=h){
if(p->data==value){
printf("%d in position %d\n",value,i);
return p;
}
i++;
p=p->next;
}printf("Value(%d) not present!\n",value);
return NULL;
}
//按位查询并删除
//偷懒所以分开写了,并且也没写按值删除
//注:出现了标记耦合
_bool DeleteByLocation(DNP h,int destin){
DeleteNode(SelectByLocation(h,destin));
// DeleteNode(SelectByValue(h,value));
}
今天的我是菜鸡吗?
是!
今天整理完数据结构书上的数据结构和算法了吗?
没有!