目录
一、前言
线性表有两种存储方式,一是顺序存储,二是链式存储。
1、顺序存储
2、链式存储
这两种存储方式各有优略,我们可以根据实际情况选择对应的存储方式。 下面,着重分析两种存储的定义和相关操作。
二、定义(顺序表与单链表)
实际中,我们会遇到各种场景,比如学生的信息存储或者图书馆书籍存储,这些需要用到数据结构。那么下面直接以图书馆的书籍为例子进行定义。
1、首先使用顺序存储的方式定义了书籍的结构体,下面定义静态分配空间方式和动态分配空间的方式的结构体。
#include<stdio.h>
#include<stdlib.h>
const int MaxSize = 12;
typedef struct books{
int id; // 书籍的ISBN
int price; // 书籍的价格,假设为整数
}book;
typedef struct Jing{ // 静态分配存储空间
book elem[MaxSize];
int length;
}JingSqList;
typedef struct Dong{ // 动态分配存储空间
book *elem;
int length;
}DongSqList;
int main()
{
JingSqList l1; // 静态
DongSqList l2; // 动态
/* 给l2的elem动态分配空间方式一 */
l2.elem = (book *)malloc(sizeof(book)*MaxSize);
free(l2.elem); // 释放空间
/* 给l2的elem动态分配空间方式二 */
l2.elem = new book();
delete l2.elem; // 释放空间
return 0;
}
2、下面采用链式存储定义书籍结构体:
#include<stdio.h>
#include<stdlib.h>
typedef struct books{
int id; // 书籍的ISBN
int price; // 书籍的价格,假设为整数
}book;
typedef struct lianshi{
book elem;
struct lianshi * next;
}LNode;
void InitList(LNode * &L){ // 采用指针引用的方式初始化链表
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL;
}
int main()
{
LNode * l;
InitList(l);
return 0;
}
三、单链表相关操作
对链表的相关操作(初始化链表、清空链表、销毁链表、获取链表的第i个元素、查找某个元素、在位置i插入元素、删除第i个节点、建立链表(分头插法/尾插法)、合并2个有序链表)。由于顺序表的操作和数组类似,比较简单,就不分析了,下面着重实现链表的这些操作。
先进行相关的定义,后面就直接展示操作的函数了:
#include<stdio.h>
#include<stdlib.h>
typedef struct books{
int id; // 书籍的ISBN
int price; // 书籍的价格,假设为整数
}book;
typedef struct lianshi{
book elem;
struct lianshi * next;
}LNode;
int main()
{
int i = 3;
int n = 12;
book e;
bool sucess;
e.id = 001; e.price = 122;
LNode * la, * lb, * lc;
InitList(la);InitList(lb); // 初始化链表
ClearList(la); // 清空链表
DestroyList(lb); // 销毁链表
insert(la,i,e); // 在第i个位置插入元素e
sucess = getElem(la, i, e); // 获得链表中第i个元素的内容
Delete(la,i); // 删除第i个节点
CreListHead(la, n); // 头插法建立链表
CreListRear(lb, n); // 尾插法建立链表
hebingList(la, lb, lc); // 合并两个有序链表
return 0;
}
1、初始化链表
void InitList(LNode * &L){ // 采用指针引用的方式初始化链表
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL;
}
2、清空链表
void ClearList(LNode * &L){ // 清空链表
LNode * p,* q;
p = L->next; // 保留头节点,从l->next开始释放
while(p){
q = p->next; // q指向下一个节点
free(p); // 释放p节点
p = q; // 继续下个节点
}
L->next = NULL;
}
3、销毁链表
void DestroyList(LNode * &L){ // 销毁单链表
LNode * p;
while(L){
p = L; // 从头节点开始释放空间
L = L->next; // 指向下一个节点
free(p);
}
}
4、获取链表第i个元素
bool getElem(LNode * L, int i, book &e){ // 获得单链表中第i个元素的内容
LNode * p = L->next;
int j = 1;
while(p && j < i){
p = p->next;
j++;
}
if(!p || j > i) // 不存在第i个元素
return false;
e = p->elem; // 找到后并存储于引用的e中
return true;
}
5、查找某个元素
LNode * FindElem(LNode * L,book &e){ // 查找某个元素,返回指针
LNode * p = L->next;
while(p && p->elem.id != e.id)
p = p->next;
return p;
}
6、在位置i插入元素
bool insert(LNode * &L,int i,book e){ // 在i的位置插入e
LNode * p = L;
int j = 0;
while(p && j < i - 1){
p = p->next;
j++;
}
if(!p || j > i - 1)return false;
LNode * s = (LNode *)malloc(sizeof(LNode));
s->elem = e;
/* 在p与p->next节点之间插入s */
s->next = p->next; // 把s放大到p的下个节点的前面
p->next = s; // 把s放到p后面
return true;
}
7、删除第i个节点
bool Delete(LNode * &L,int i){ // 删除第i个节点
LNode * p = L;
int j = 0;
while(p->next && j < i - 1){
p = p->next;
j++;
}
if(!p->next || j > i - 1)
return false;
LNode * q = p->next; // 临时保存即将被删除的节点q,以便释放
p->next = q->next; // 把即将被删除节点的后续接到前面的节点
free(q); // 释放节点q
return true;
}
8、建立链表(头插法 & 尾插法)
void CreListHead(LNode * &L, int n){ // 头插法建立链表
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL;
for(int i = 0;i < n;++i){
LNode * p = (LNode *)malloc(sizeof(LNode));
scanf("%d%d",&p->elem.id,&p->elem.price);
p->next = L->next; // 插到首元节点前面
L->next = p; // 新节点p接到头节点L后面
}
}
void CreListRear(LNode * &L, int n){ // 尾插法建立链表
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL;
LNode * r = (LNode *)malloc(sizeof(LNode)); // 始终有一个尾指针
for(int i = 0;i < n;++i){
LNode * p = (LNode *)malloc(sizeof(LNode));
p->next = NULL;
scanf("%d%d",&p->elem.id,&p->elem.price);
r->next = p; // 插入到链表尾部
r = p; // r指针一直指着尾部
}
}
9、合并两个有序链表(La和Lb)
开始需要有pa、pb分别指向La和Lb的首元节点(pa = La->next,pb = Lb->next),我们新建一个表头Lc。让Lc接到La处,同样有个pc指向Lc(Lc = La,pc = Lc。此处注意和pa、pb初始化不同)
比较谁的数据小,就把其接到pc(如pc->next = pa)后面,然后让pc = pc->next,再让小的那个指针往后移动到下个节点(如pa = pa->next),直到某个链表所有元素都插入到了Lc。
最后把剩下的部分接到pc后面。代码如下:
void hebingList(LNode * &La,LNode * &Lb,LNode * &Lc){ // 合并两个有序链表
LNode *pa = La->next; // La的尾节点
LNode *pb = Lb->next; // Lb的尾节点
LNode *pc = Lc = La; // 用La的头节点作为Lc的头节点
while(pa && pb){
if(pa->elem.id <= pb->elem.id){
pc->next = pa;
pc = pa;
pa = pa->next;
}
else{
pc->next = pb;
pc = pb;
pb = pb->next;
}
pc->next = pa ? pa : pb; // 把剩余的部分接到pc后面
free(Lb); // 释放Lb的头节点
}
}