前天停电摆一天,昨天琐事忙一天,寄。
线性表:
线性表(List):线性的,依次的,一列
较复杂的线性表中数据元素可以有若干个数据项组成。
两种实现方法:数组(顺序存储)和链表(链式存储),显然,链表实现更加具有效率。
下面我们讨论链表的线性表实现:
链表中第一个结点(Node)的存储位置叫做头指针,最后一个指针指向NULL(无后继指针)。
头指针前存在头结点,一般存储公共信息。
头指针与头结点:
逻辑示意图(非内存存储方式!):
结点由存放数据元素的数据域和存放后继元素地址的指针域组成。
代码实现:
1.建立单链表
typedef struct Node{
int value;
struct Node* next;
}*Link;
//Link=(strcut) Node*
1.1头插法:
void CreatLinkHead(Link head,int lenth){
Link newNode;
head=(Link)malloc(sizeof(struct Node));
head->next=NULL;
while(lenth--){
newNode=(Link)malloc(sizeof(struct Node));
newNode->value=rand()%100+1;
newNode->next=head->next;
head->next=newNode;
}
}
1.2尾插法:
void CreatLinkRear(Link head, int lenth) {
Link rear, newNode;
head = (Link)malloc(sizeof(struct Node));
head->next = NULL;
rear = head;
while (lenth--) {
newNode = (Link)malloc(sizeof(struct Node));
newNode->value = rand() % 100 + 1;
newNode->next = rear->next;
rear->next = newNode;
rear = newNode;
}
rear->next = NULL; //也许可以不要?毕竟每次循环结束时rear->next始终为NULL
}
2.遍历
void TraverseList(Link head) {
Link p = head->next;
while (p) {
printf("%d ", p->value);
p = p->next;
}
puts("");
}
3.插入:
int InsertList(Link L, int pos, int e) {
int j = 1;
Link p/*pointer*/, s/*newNode*/;
p = L;
while (p && (j < pos)) {
p = p->next;
++j;
}//找寻此位置,且只需找到此位置的前驱即可,因为前驱包含指向后驱的指针
if (!p || j > pos) return 0;/*ERROR,不存在此位置*/
//存在则插入
s = (Link)malloc(sizeof(struct Node));
s->value = e;
s->next = p->next;
p->next = s;
return 1;/*OK,成功插入*/
}
4.删除:
int ListDelete(Link L, int pos, int* e/*记录删除的数据的值*/) {
int j = 1;
Link p, q/*temp*/;
p = L;
while (p->next && j < pos) {
p = p->next;
++j;
}//这里和插入不同,判断条件p变为p->next
//原因为插入无需保证p的后驱不为空,而删除需要保证p的后驱不为空
if (!(p->next) || j > pos) return 0;
q = p->next;//将p的后驱单独拿出(复制)
p->next = q->next;//如果不需要知道所删除的数据,那么这三行可以改为如下代码
// p->next=p->next->next;//然而这行代码无法释放p->next所占用的内存
*e = q->value;
free(q);
return 1;
}
5.释放单链表
int ClearList(Link L) {
Link p, q;
p = L->next;
while (p) {
q = p->next;
free(p);
p = q;
}
/*
显然,以下代码不可行
free(p);
p=p->next;
已经释放p后,如何再通过p寻找它的后继呢?须知free(p)包含释放数据域和指针域。
*/
L->next = NULL;
return 1;
}
总代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
int value;
struct Node* next;
}*Link;
//Link=(strcut) Node*
void CreatLinkHead(Link head, int lenth) {
Link newNode;
head = (Link)malloc(sizeof(struct Node));
head->next = NULL;
while (lenth--) {
newNode = (Link)malloc(sizeof(struct Node));
newNode->value = rand() % 100 + 1;
newNode->next = head->next;
head->next = newNode;
}
}
void CreatLinkRear(Link head, int lenth) {
Link rear, newNode;
head = (Link)malloc(sizeof(struct Node));
head->next = NULL;
rear = head;
while (lenth--) {
newNode = (Link)malloc(sizeof(struct Node));
newNode->value = rand() % 100 + 1;
newNode->next = rear->next;
rear->next = newNode;
rear = newNode;
}
rear->next = NULL; //也许可以不要?毕竟每次循环结束时rear->next始终为NULL
}
void TraverseList(Link head) {
Link p = head->next;
while (p) {
printf("%d ", p->value);
p = p->next;
}
puts("");
}
int InsertList(Link L, int pos, int e) {
int j = 1;
Link p/*pointer*/, s/*newNode*/;
p = L;
while (p && (j < pos)) {
p = p->next;
++j;
}
if (!p || j > pos) return 0;/*ERROR*/
s = (Link)malloc(sizeof(struct Node));
s->value = e;
s->next = p->next;
p->next = s;
return 1;/*OK*/
}
int ListDelete(Link L, int pos, int* e/*记录删除的数据的值*/) {
int j = 1;
Link p, q/*temp*/;
p = L;
while (p->next && j < pos) {
p = p->next;
++j;
}//这里和插入不同,判断条件p变为p->next
//原因为插入无需保证p的后驱不为空,而删除需要保证p的后驱不为空
if (!(p->next) || j > pos) return 0;
q = p->next;//将p的后驱单独拿出(复制)
p->next = q->next;//如果不需要知道所删除的数据,那么这三行可以改为如下代码
// p->next=p->next->next;//然而这行代码无法释放p->next所占用的内存
*e = q->value;
free(q);
return 1;
}
int ClearList(Link L) {
Link p, q;
p = L->next;
while (p) {
q = p->next;
free(p);
p = q;
}
/*
显然,以下代码不可行
free(p);
p=p->next;
已经释放p后,如何再通过p寻找它的后继呢?须知free(p)包含释放数据域和指针域。
*/
L->next = NULL;
return 1;
}
int main() {
return 0;
}
另,单链表基本操作见下:
(5条消息) C语言单链表实现初始化、创建、增、删、查等基本操作(详细)_Qian_Qian_IT的博客-CSDN博客
typedef详解请移步下面这篇文章:
(5条消息) C语言typedef的用法详解_风叶翩翩的博客-CSDN博客_typetef
单链表的闲谈可以看这篇博客:
(5条消息) 对单链表的理解_wjc96815的博客-CSDN博客_数组单链表难点
数组模拟单链表感兴趣的可以自己去搜索下,这里不再展开。
循环链表:
将尾指针指向头
改进:
此时,rear->next->next==head->next
完成这两个循环链表的合并只需进行如下操作:
p = rearA->next;
rearA->next = rearB->next->next;
rearB->next = p;
free(p);
双向链表:
在单链表的每个结点中在设置一个指向其前驱的指针域。
1.双向链表的插入操作:
Link p, s;
// p->prior->next == p == p->next->prior; //三者相等
//先解决前驱,再解决后继,并且两者交叉排列,这样的思路更连贯一些
s->prior = p;
s->next = p->next;
p->next->prior = s;
p->next = s;
2.删除操作:
p->next->prior=p->prior;
p->prior->next=p->next;
free(p);
//这里无需引入中间变量再进行free操作,可以直接释放