二、线性表
1、线性表的顺序存储实现
#define MAXSIZE 10
typedef int ElementType;
typedef struct LNode* List;
struct LNode{
ElementType Data[MAXSIZE];
int Last;//最后一位数据的下标索引
};
//初始化
List MakeEmpty(){
List PtrL;
PtrL = (List)malloc(sizeof(struct LNode));
PtrL->Last = -1;
return PtrL;
}
//查找
int Find(ElementType x, List PtrL){
int i = 0;
while(i <= PtrL->Last && PtrL->Data[i] != x)
i++;
if(i > PtrL->Last) return -1; //没有找到返回-1
else return i; //找到返回下标
}
//插入(这里的i是从1开始的)(实际是Data[i-1]插入x)
void Insert(ElementType x, int i, List PtrL){
int j;
if(PtrL->Last == MAXSIZE-1){//表空间满了,无法插入
printf("表满");
return;
}
if(i < 1 || i > PtrL->Last+2){
printf("位置不合法");
return;
}
for(j = PtrL->Last; j >= i-1; j--)//原数组的a[i-1]..a[last]后移1位
PtrL->Data[j+1] = PtrL->Data[j];
PtrL->Data[i-1] = x; //插入x
PtrL->Last++; //更新长度
return;
}
//删除(i从1开始)(想要删除的元素,可以在此基础上改)
void Delete(int i, List PtrL){
int j;
if(i < 1 || PtrL->Last+1){
printf("删除位置非法");
return;
}
for(j = i; j <= PtrL->Last; j++)//a[i]..a[last]前移
PtrL->Data[j-1] = PtrL->Data[j];
PtrL->Last--; //更新长度
return;
}
2、线性表的链式存储实现
typedef int ElementType;
typedef struct LNode* List;
struct LNode{
ElementType Data;
List Next;
};
//求表长
int Length(List PtrL){
List p = PtrL;
int j = 0;
while(p){
p = p->Next;
j++;
}
return j;
}
//按序号查找
List FindKth(int k, List PtrL){
List p = PtrL;
int i = 1;
while(p != NULL && i < k){
p = p -> Next;
i++;
}
if(i == k) return p;//找到,返回结点指针
else return NULL; //否则返回空
}
//按值查找
List Find(ElementType x, List PtrL){
List p = PtrL;
while(p != NULL && p->Data != x)
p = p->Next;
return p;
}
//插入
List Insert(ElementType x, int i, List PtrL){
List p, s;
if(i == 1){ //新结点在表头插入
s = (List)malloc(sizeof(struct LNode));
s->Data = x;
s->Next = PtrL;
return s;
}
p = FindKth(i-1, PtrL); //查找第i-1个结点
if(p == NULL){ //第i-1个结点不存在,不能插入
printf("插入位置错误");
return NULL;
} else{
s = (List)malloc(sizeof(struct LNode));
s->Data = x;
s->Next = p->Next;
p->Next = s;
return PtrL;
}
}
//删除
List Delete(int i, List PtrL){
List p, s;
if(i == 1){ //删除首结点
s = PtrL;
if(PtrL != NULL) PtrL = PtrL->Next;//修改指向
else return NULL;
free(s); //清空首结点的空间
return PtrL;
}
p = FindKth(i-1, PtrL); //查找i-1结点
if(p == NULL){
printf("结点不存在");
return NULL;
} else if(p->Next == NULL){
printf("想要删除的结点不存在");
return NULL;
} else {
s = p->Next; //s指向i结点
p=Next = s->Next; //从链表中摘除
free(s); //释放被删除结点
return PtrL;
}
}
3、广义表结构
引入:
假设有个二元多项式:P(x,y) = 9x12y2 + 4x12 + 15x8y3 - x8y + 3x2
然后可以看成以x为变量的一元多项式:
P(x,y) = (9y2 + 4)x12 + (15y3 - y)x8 + 3x2
接着就可以统一成图中的结点结构,在普通线性表的基础上,用 tag 区分是常数系数,还是多项式。然后 union 把这两种统一起来。
结构图:
typedef int ElementType;
typedef struct GNode* GList;
struct GNode{
int Tag; //标志:0表示结点是单元素,1表示结点是广义表
union { //联合体,单元素Data 和子表指针SubList 共用空间
ElementType Data;
GList SubList;
}URegion;
GList Next; //指向后继节点
}
4、多重链表
链表中的结点可能同时隶属多个链。
4.1 稀疏矩阵(十字链表)
只存储矩阵中的非0项:结点数据包含(row, col, Value)
同时结点有两个指针:行指针Right, 列指针 Down。
蓝色框里的是稀疏矩阵入口,记录的数据是行数、列数、非零项个数。
然后总共有两种结点,一种Head头结点,一种Term存数据的,都能down和Right。
两种结点很像,所以可以使用union来设计。
- 用一个标志 tag 来区分头结点和数据结点;
- 头结点的标识值为”Head“,非0元素结点的标识值为“Term”。
三、栈
1、栈的顺序存储实现
#define MAXSIZE 100
typedef int ElementType;
typedef struct SNode* Stack;
struct SNode{
ElementType Data[MAXSIZE];
int Top;
};
//入栈
void Push(Stack PtrS, ElementType item){
if(PtrS->Top == MAXSIZE-1){
printf("栈满"); return;
} else{
PtrS->Data[++(PtrS->Top)] = item;
return;
}
}
//出栈
ElementType Pop(Stack PtrS){
if(PtrS->Top == -1){
printf("栈空");
return ERROR; //ERROR是ElementType的错误值
} else
return (PtrS->Data[(PtrS->Top)--]);
}
2、栈的链式存储实现
typedef int ElementType;
typedef struct SNode* Stack;
struct SNode{
ElementType Data;
struct SNode* Next;
};
//初始化头结点
Stack CreateStack(){
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
//判断是否为空
int IsEmpty(Stack S){
return (S->Next == NULL);
}
//入栈(头插法)
void Push(ElementType item, Stack S){
struct SNode* newNode;
newNode = (struct SNode*)malloc(sizeof(struct SNode));
newNode->Data = item;
newNode->Next = S->Next;
S->Next = newNode;
}
//出栈
ElementType Pop(Stack S){
struct SNode* FirstNode;
ElementType TopElem;
if(IsEmpty(S)){
printf("栈空");
return NULL;
} else{
FirstNode = S->Next;
S->Next = FirstNode->Next;
TopElem = FirstNode->Data;
free(FirstNode);
return TopElem;
}
}
3、栈的应用:表达式求值
3.1 中缀表达式转换成后缀表达式
a * ( b + c ) / d
转换成后缀表达式为: a b c + * d /
- 碰到数字就输出
- 碰到运算符就比较优先级,高压栈,低出栈。如果是右括号,一直出栈,直到左括号出栈。
3.2 求后缀表达式
直接从左到右扫描,遇到运算符,弹两个数出来,结果重新压栈。
3.3 栈的其他应用
- 函数调用及递归实现
- 深度优先搜索
- 回溯算法
- 。。。
四、队列
1、队列的顺序存储实现
循环队列。空一格。入队处理rear,出队修改front。
#define MAXSIZE 100
typedef int ElementType;
struct QNode{
ElementType Data[MAXSIZE];
int rear;
int front;
};
typedef struct QNode* Queue;
//入队
void AddQ(Queue PtrQ, ElementType item){
if((PtrQ->rear+1) % MAXSIZE == PtrQ->front){
printf("队列满");
return;
}
PtrQ->rear = (PtrQ->rear+1) % MAXSIZE;
PtrQ->Data[PtrQ->rear] = item;
}
//出队
ElementType DeleteQ(Queue PtrQ){
if(PtrQ->front == PtrQ->rear){
printf("队列空");
return ERROR;
} else {
PtrQ->front = (PtrQ->front+1) % MAXSIZE;
return PtrQ->Data[PtrQ->front];
}
}
2、队列的链式存储实现
typedef int ElementType;
struct Node{ //链表
ElementType Data;
struct Node* Next;
};
struct QNode{ //队列结构
struct Node* rear;//队尾结点
struct Node* front;//队头结点
};
typedef struct QNode* Queue;
//不带头结点的出队
ElementType DeleteQ(Queue PtrQ){
struct Node* FrontNode;
ElementType FrontElem;
if(PtrQ->front == NULL){
printf("队列为空"); return ERROR;
}
FrontNode = PtrQ->front;
if(PtrQ->front == PtrQ->rear)//如果只有一个元素
PtrQ->front = PtrQ->rear = NULL;//删除队列
else
PtrQ->front = PtrQ->front->Next;
FrontElem = FrontNode->Data;
free(FrontNode);//释放结点
return FrontElem;
}
//入队
void AddQ(ElementType elem, Queue PtrQ){
struct Node* newNode;//初始化新结点
newNode->Data = elem;
newNode->Next = NULL;
if(PtrQ->front == NULL) //队列还是空的,新增第一个结点
PtrQ->front = PtrQ->rear = newNode;
else
PtrQ->rear->next = newNode;
}