线性表基本算法
线性表
1. 顺序表
结构体
静态分配 (下标从1开始)
#define MAXSIZE 50
typedef struct {
ElemType data[MAXSIZE]; // 顺序表元素
int length; // 当前长度
} SqList;
动态分配
#define INITSIZE 50
typedef struct {
ElemType *data; // 顺序表元素
int maxsize,length; // 前者为最大长度. 后者为当前长度
} SqList;
操作
数组下标从0开始, 顺序表下标从1开始,所以要对i减一
(1) 插入 O(n)
bool insert (SqList &L, int i, ElemType e){
if(i < 1 || i > L.length +1 || L.length >= MAXSIZE) return false; // 合法性校验
for(int j = L.length; j >= i; j--)
L.data[j] = L.data[j-1]; // 将第i个元素之后的元素后移
L.data[i-1] = e;
L.length ++; // 当前表长+1
return true;
}
(2) 删除 O(n)
// 删除第i个元素, 并用引用e返回
bool delete (SqList &L, int i, ElemType &e){
if(i < 1 || i > L.length) return false; // 要删除位置合法性校验
e = L.data[i-1]; // 将要删除的元素用e返回
for(int j = i ; j < L.length; j++)
L.data[ j - 1 ] = L.data[ j ]; // 将第i个元素之后的元素后移
L.length --; // 当前表长+1
return true;
}
(3) 查找(按值) O(n)
返回位序
int find(SqList L, ElemType e){
for(int i = 0; i < L.length; i++){
if(L.data[i] == e) return i+1; // 将位序i+1返回
}
return -1; // 查找失败
}
应用
(1) 将顺序表中所有元素原地逆置
要点: 找到枢轴, 交换枢轴左右对称的元素
**对称元素下标关系: A[i] = A[n-i-1]; **
void reverse(int A[],int n){
for(int i = 0; i < n/2; i++){
// 交换左右元素
int tmp = A[i];
A[i] = A[n-i-1]; // 对称元素下标关系
A[n-i-1] = tmp;
}
}
2. 单链表
结构体
typedef struct LNode{
ElemType data; // 数据域
struct LNode *next; // 指针域
}LNode, *LinkList;
操作
(1) 头插法建立单链表 O(n)
LinkList headInsert (LinkList &L){
LNode *s;
int x;
L = (LinkList) malloc(sizeof (LNode)); // 创建头结点
L->next = NULL; // 初始化为空表
scanf("%d", &x);
while(x != 9999){
s = (LNode*)malloc(sizeof(LNode)); // 创建新节点
s->data = x;
// 将新节点插入
s->next = L->next;
L->next = s;
scanf("%d", &x); // 继续输入
}
return L;
}
要点: 头插法基本操作
L是头结点
s->next = L->next;
L->next = s;
(2) 尾插法建立单链表 O(n)
LinkList tailInsert(LinkList &L){
LNode *s, *r = L; // r表示表尾指针
int x;
L = (LinkList) malloc(sizeof (LNode)); // 创建头结点
L->next = NULL; // 初始化为空表
scanf("%d", &x);
while(x != 9999){
s = (LNode*)malloc(sizeof(LNode)); // 创建新节点
s->data = x;
// 将新节点插入
r->next = s;
r = s;
scanf("%d", &x); // 继续输入
}
return L;
}
尾插法关键: 设置表尾结点
**r->next = s;
r = s; **
(3) 按序号查找 O(n)
注意位序从1开始
LNode* getElem(LinkList L, int i){
if(L == NULL || i <= 0) return NULL; // 合法性判断
LNode *p = L->next; // 设置工作指针
int j = 1;
while(p && j < i){
// 查找第i个结点
p = p->next;
j++;
}
return p;
}
(4) 按值查找 O(n)
LNode* findElem(LinkList L, ElemType e){
if(L == NULL) return;
LNode* p = L->next;
while(p != NULL && p->data != e){
p = p->next;
}
return p;
}
(5) 前插 [时间复杂度为O(1)的版本]
要点: 采用后插法插入后, 将前驱的数据域与新插入的数据域交换
// s插入到p后
s->next = p->next
p->next = s;
// 交换数据域
tmp = p->data;
p->data = s->data;
s->data = tmp;
(6) 有前驱删除 O(n)
p = getElem(L,i-1); // 找到要删除的前驱
q = p->next; // q是想要被删除的结点
p->next = q->next; // 将q从链表中断开
free(q);
(7) 无前驱删除 O(1)
要点: 将后驱结点的值赋值给自身,然后删除后驱
q = p->next;
p->data = q->data; // 赋值后驱的指针域
p->next = q->next;
free(q);
应用
删除所有值为X的结点
- 不带头结点 (L也有数据域), 可以改成无前驱删除
while版本
void deleteX(LinkList &L, ElemType X){
LinkList p,q = L;
p = L->next;
while(p != NULL){
if(p->data == X){
q->next = p->next; // 暂存后继防止断链
free(p);
p = q->next;
} else {
q = p;
p = p->next;
}
}
if(L->data == X){ // 处理头结点
q = L;
L = L->next;
free(q);
}
}
- 递归版本
void deleteX(LinkList L, ElemType X){
if(L == NULL) return ; // 递归出口
LNode* q = NULL;
if(L->data == X){
// 这两行也可以写成 q = L;
q = L;
L = L->next; // L先移向下一个结点,再删除q
free(q);
deleteX(L,X);
} else {
deleteX(L->next,X);
}
}
- 带头结点
void deleteX(LinkList L, ElemType e){
LinkList *p = L->next, *q;
while(p != NULL){
if(p->data == X){
// 无前驱删除
q = p->next;
p->data = q->data;
p->next = q->nextl
free(q);
}
p = p->next;
}
// 无需处理头结点
}
逆转单链表 O(n)
- 带头节点
LinkList reverse(LinkList &L){
// pre表示前驱,r表示后置
LNode *pre,*p = L->next,*r = p->next;
p->next = NULL; // 处理第一个节点
while(r != NULL){ // r为空时表示最后一个节点
// 下面三行表示依次继续遍历,相当于pqr同时后移一位
pre = p;
p = r;
r = r->next;
p->next = pre; // 指针反转
}
L->next = p; // 处理最后一个节点
return L;
}
递归复制线性表
LinkList copy(LinkList list){
// 递归出口
if(list1 == NULL) return NULL;
LinkList list2;
// 前序位置操作
list2 = (LinkList)malloc(sizeof(LNode));
list2->data = list->data;
list2->next = copy(list->next); // 递归向下
return list2;
}
合并两个有序线性表
LinkList merge(LinkList list1,LinkList list2){
LinkList L3,p = L1,q = L2,r; // r是L3的尾指针
// 处理第一个节点
if(L1->data <= L2->data){
L3 = L1;
r = L1;
p = L1->next;
} else {
L3 = L2;
r = L2;
q = L2->next;
}
// 链接后续节点
while(p != NULL && q != NULL){
if(p->data <= q -> data){
r->next = p; // 将较小元素链接到r
r = p; // r后移到最后一个节点
p = p->next; // L2的工作指针后移
} else {
r->next = q;
r = q;
q = q->next;
}
}
r->next = (p != NULL ? p:q); // 将剩下未遍历完成的链表链接到L3
return L3;
}