线性表
定义
- 表中元素个数称为线性表的长度
- 线性表没有元素时,称为空表
- 表起始位置称为表头,表结束位置称表尾
- 用来存储由n个元素构成的有序序列
操作
List MakeEmpty()
: 初始化一个空线性表 LElementType FindKth(int K,List L)
:根据位序 K,返回相应元素int Find(ElementType X,List L)
:在线性表 L 中查找 X 的第一次出现位置void Insert(ElementType X,int i,List L)
:在位序 i 前插入一个新元素 Xvoid Delete(int i,List L)
:删除指定位序 i 的元素int Length(List L)
:返回线性表 L 的长度 n
线性表的实现
顺序存储
- 利用数组的连续储存空间顺序存放线性表的各元素,各元素的物理地址是连续的。
#define MAXSIZE 100//Data数组大小
typedef int ElementType;//元素类型 可定义为任意类型
typedef struct LNode* List;
struct LNode
{
ElementType Data[MAXSIZE];
int Last;//线性表的最后一个元素的下标
};
List L;
//访问下标为 i 的元素:L->Data[i]
//线性表的长度:L->Last+1
- 初始化操作
List MakeEmpty() {
List L;
L = (List)malloc(sizeof(struct LNode));//为顺序表分配内存
L->Last = -1;//顺序表是空的
return L;
}
- 按值查找操作
int Find(ElementType X, List L) {
int i = 0;
while (i <= L->Last && L->Data[i] != X) {//如果找到x就跳出循环 没找到x就遍历
i++;
}
if (i > L->Last) {//没找到 返回-1
return -1;
}
else return i;//找到 返回储存位置
}
- 插入操作(在第i个位置插入新元素前 需将i以后的元素都后移一位 腾出位置)
void Insert(ElementType X, int i, List L) {
if (L->Last == MAXSIZE - 1) {//表空间已满 无法插入
printf("表满无法插入");
return;
}
if (i < 0 || i > L->Last + 1) {//检查插入位置的合法性
printf("位置不合法");
return;
}
for (int j = L->Last; j >= i; j--) {//从后往前依次向后移一位 给新元素腾出位置
L->Data[j + 1] = L->Data[j];
}
L->Data[i] = X;//插入新元素
L->Last++;//Last仍指向最后元素
}
- 删除操作(删除第i位的元素后 需要将i以后的元素向前移一位 补上空缺)
void Delete(int i, List L) {//删除
if (i < 0 || i > L->Last) {//检查空表及删除位置的合法性
printf("不存在第%d个元素", i);
return;
}
for (int j = i; j <= L->Last; j++) {//将i位后的元素依次向前移一位 补上空缺
L->Data[j] = L->Data[j + 1];
}
L->Last--;//Last指向最后元素
return;
}
- 按序查找操作
ElementType FindKth(int K, List L) {
if (K < 0 || K > L->Last) {//位置越界
printf("L->Data[%d]不存在元素", K);
return;
}
return L->Data[K];
}
- 返回表长操作
int Length(List L){
return L->Last+1;
}
链式存储
- 不要求物理空间上相邻,只通过指针来建立逻辑的链条,因此删除插入等操作只需修改链而无需移动数据元素。
typedef int ElementType;//元素类型 可定义为任意类型
typedef struct LNode* List;
struct LNode
{
ElementType Data;
List Next;//下一个节点的指针
};
List L;
- 初始化操作
List MakeEmpty() {
List L = (List)malloc(sizeof(struct LNode));
L = NULL;
return L;
}
- 按值查找操作
List Find(ElementType X, List L) {
List p = L;
while (p != NULL && p->Data != X) {
p = p->Next;
}
return p;
// 找到了返回p
// 未找到返回NULL 此时p等于NULL
}
- 按序查找操作
List FindKth(int K, List L) {
List p = L;
int i = 1;
while (p != NULL && i < K) {
p = p->Next;
i++;
}
if (i == K) return p;//找到了 返回p
else return NULL;//没找到 返回空指针
}
- 返回表长操作
int Length(List L) {
List p = L;//p指向列表的第一个节点
int j = 0;
while (p) {
p = p->Next;//当前p指向的是第j个节点
j++;
}
return j;
}
- 插入操作 在第i个节点的位置插入一个新节点 注意后两步顺序不能颠倒
- 用s指向一个新节点
- 用p指向第i-1个节点
- 把第i个节点作为s的下一个节点
- 把s作为p的下一个节点
List Insert(ElementType X, int i, List L) {
List s, p;
if (i == 1) {//在表头插入新节点
s = (List)malloc(sizeof(struct LNode));
s->Data = X;
s->Next = L;
return s;
}
p = FindKth(i - 1, L); //查找第i - 1个节点
if (p == NULL) {//第i-1个节点不存在
printf("参数i错误");
return NULL;
}
else {
s = (List)malloc(sizeof(struct LNode));//申请新节点
s->Data = X;
s->Next = p->Next;//新节点插入在第i-1个节点的后面
p->Next = s;
return L;
}
}
- 删除操作 删除第i个节点
- p指向第i-1个节点
- s指向第i个节点
- 把s的下一个节点作为p的下一个节点
- 释放s的内存空间
List Delete(int i, List L) {//删除第i个节点
List p, s;
if (i == 1) {//
s = L;
if (L->Next != NULL) L = L->Next;
else return NULL;
}
p = FindKth(i - 1, L);//查找第i - 1个节点
if (!p || !(p->Next)) {//第i个或第i-1个节点不存在
printf("结点错误");
return NULL;
}
else {
s = p->Next;//s指向第i个节点
p->Next = s->Next;//删除节点
free(s);//释放删除节点的内存空间
return L;
}
}
广义表与多重链表
- 广义表是线性表的推广,其元素不仅可以是单元素,也可以是另一个广义表
- 多重链表中的节点隶属于多个链,其节点有多个指针域(如图、树),但双向链表不是多重链表
堆栈Stack
定义
- 具有一定操作约束的有穷线性表,只在一端(栈顶 Top)作插入、删除,类似于摞盘子
- 插入数据称为入栈Push,删除数据称为出栈Pop
操作
Stack CreateStack(int MaxSize)
:生成空堆栈,其最大长度为 MaxSizeint IsFull(Stack S,int MaxSize)
:判断堆栈 S 是否已满void Push(Stack S,ElementType item)
:将元素 item 压入堆栈int IsEmpty(Stack S)
:判断堆栈 S 是否为空ElementType Pop(Stack S)
:删除并返回栈顶元素
堆栈的实现
顺序存储
- 由一个一维数组和记录栈顶元素位置的变量组成
#define MaxSize 100//堆栈元素的最大个数
typedef int ElementType;//元素类型 可定义为任意类型
typedef struct SNode* Stack;
struct SNode {
ElementType Data[MaxSize];
int Top;//堆栈的栈顶指针
};
- 初始化堆栈
Stack CreateStack() {
S = (Stack)malloc(sizeof(struct SNode));
S->Top = -1;//-1表示堆栈为空
return S;
}
- 是否已满
int IsFull(Stack S) {
return (S->Top == MaxSize - 1);
}
- 是否为空
int IsEmpty(Stack S) {
return (S->Top == -1);
- 入栈操作
void Push(Stack S, ElementType item) {
if (IsFull(S)) {
printf("堆栈已满");
return;
}
else {
S->Top++;//栈顶向上移一位
S->Data[S->Top] = item;//放入栈顶
return;
}
}
- 出栈操作
ElementType Pop(Stack S) {
if (IsEmpty(S)) {
printf("堆栈为空");
return NULL;
}
else {
ElementType val = S->Data[S->Top];//取出栈顶
S->Top--;//栈顶向下移一位
return val;
}
}
实例:请用一个数组实现两个堆栈,要求最大限度利用数组空间,使数组只要有空间入栈就可以操作成功
思路:使这两个栈分别从数组两头向中间生长,当两个栈的顶端指针相遇时,表示这两个栈都满了
链式存储
- 用一个单链表(栈链)实现,插入和删除只在栈链的栈顶上进行。
- 指针在栈顶,元素在栈顶后面。
- 链表实现的堆栈是没有上限的,因此无需查看是否已满。
typedef int ElementType;//元素类型 可定义为任意类型
typedef struct SNode* Stack;
struct SNode {
ElementType Data;//存放元素
struct SNode* Next;//指向下一元素
};
Stack S;
- 初始化堆栈
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(Stack S, ElementType item) {//将元素item压入堆栈S
struct SNode* TmpCell;//TmpCell是要压入的元素
TmpCell = (Stack)malloc(sizeof(struct SNode));
TmpCell->Data = item;
TmpCell->Next = S->Next;//新入栈的元素在栈顶S后面
S->Next = TmpCell;
}
- 出栈操作
ElementType Pop(Stack S) {//删除并返回堆栈S的栈顶元素
if (IsEmpty(S)) {
printf("堆栈为空");
return NULL;
}
struct SNode* Fiest;//First是栈顶元素
ElementType val;
Fiest = S->Next;//出栈的元素在栈顶S后面
S->Next = Fiest->Next;//删除第一个元素
val = Fiest->Data;//取出被删除元素的值
free(Fiest);//释放空间
return val;
}
堆栈的应用
- 递归实现
- 深度优先搜索
- 回溯算法
- 中缀表达式求值
- ...
我们主要关注中缀表达式求值的问题
思路:将中缀表达式转换为后缀表达式,然后求值
队列Queue
定义
- 具有一定操作约束的线性表,在一端插入,另一端删除,先进先出,类似于排队
- 插入数据称为入队列,删除数据称为出队列
操作
Queue CreateQueue(int MaxSize)
:生成长度为 MaxSize 的空队列int IsFull(Queue Q)
:判断队列 Q 是已满void AddQ(Queue Q,ElementType item)
:将数据元素 item 插入队列 Q 中int IsEmpty(Queue Q)
:判断队列 Q 是否为空ElementType DeleteQ(Queue Q)
:将队头数据元素从队列中删除并返回
实现
顺序存储
- 由一个一维数组和两个变量组成,front指向队列第一个元素前面,rear指向队列最后一个元素,从rear入队,从front出队。由于无法区分队列是满还是空,因此仅使用n-1个数组空间。
#define MaxSize 100
typedef int ElemeentType;
typedef struct QNode* Queue;
struct QNode {//初始化
ElemeentType Data[MaxSize];
int rear;
int front;
};
- 初始化操作
Queue CreateQueue() {
Queue Q;
Q = (Queue)malloc(sizeof(struct QNode));
Q->front = -1;
Q->rear = -1;
return Q;
}
- 是否已满
int IsFull(Queue Q) {
return ((Q->rear + 1) % MaxSize == Q->front);
}
- 是否为空
int IsEmpty(Queue Q) {
return (Q->front == Q->rear);
}
- 新元素入队
void AddQ(Queue Q, ElemeentType item) {
if (IsFull(Q)) {
printf("队列满");
return;
}
Q->rear = (Q->rear + 1) % MaxSize;
Q->Data[Q->rear] = item;
}
- 队首元素出队
ElemeentType DeleteQ(Queue Q) {//元素队首出队
if (IsEmpty(Q)) {
printf("队列空");
return NULL;
}
Q->front = (Q->front + 1) % MaxSize;
return Q->Data[Q->front];
}
链式存储
- 用一个单链表实现队列,插入和删除操作分别在链表的两头进行,链表不会满因此无需判断队列是否已满
typedef int ElementType;
typedef struct QNode* Queue;
struct Node {
ElementType Data;
struct Node* Next;
};
struct QNode {//链队列结构
struct Node* rear;//指向队尾节点
struct Node* front;//指向队首节点
};
Queue Q;
- 初始化操作
Queue CreateQueue() {
Queue Q;
Q = (Queue)malloc(sizeof(struct QNode));
Q->front = NULL;
Q->rear = NULL;
return Q;
}
- 是否为空
int IsEmpty(Queue Q){ return (Q->front == NULL); }
- 新元素从队尾入队
void AddQ(Queue Q, ElementType item) {//新元素从队尾入队
struct Node* node;
node = (struct Node*)malloc(sizeof(struct Node));
node->Data = item;
node->Next = NULL;
if (Q->rear == NULL) { //此时队列空
Q->rear = node;
Q->front = node;
}
else {//队列不为空
Q->rear->Next = node; //将结点入队
Q->rear = node; // rear 仍指向最后
}
}
- 元素从队首出队
ElementType DeleteQ(Queue Q) {
struct Node* FrontCell;
ElementType FrontElem;
if (IsEmpty(Q)) {
printf("队列空");
return 0;
}
FrontCell = Q->front;
if (Q->front == Q->rear) { // 队列中只有一个元素
Q->front = Q->rear = NULL;//删除后队列为空
}
else {
Q->front = Q->front->Next;
}
FrontElem = FrontCell->Data;
free(FrontCell);//释放被删除节点空间
return FrontElem;
}
应用
多项式加法运算
- 采用不带头节点的单项链表,按照指数递减的顺序排列各项
- 思路:相同指数的项系数相加,其余部分进行拷贝
#include<stdio.h>
#include<malloc.h>
struct PolyNode {
int coef;//系数
int expon;//指数
struct PolyNode* link;//指向下一个结点的指针
};
typedef struct PolyNode* Polynomial;
Polynomial P1, P2;//待处理的两个多项式
int Compare(int e1, int e2) {//比较两个多项式中某一项的系数
if (e1 > e2) return 1;
else if (e1 = e2) return 0;
else return -1;
}
void Attach(int c, int e, Polynomial* pRear) {//添加结果新节点
Polynomial P;
P = (Polynomial)malloc(sizeof(struct PolyNode));
P->coef = c;//对新结点赋值
P->expon = e;
P->link = NULL;
(*pRear)->link = P;//在原结果后拼接新节点
*pRear = P;//尾指针指向新节点
}
Polynomial PolyAdd(Polynomial P1, Polynomial P2) {
Polynomial front, rear, temp;
int sum;
rear = (Polynomial)malloc(sizeof(struct PolyNode));//为方便拼接处理 初始为rear申请一个空节点
front = rear;//front指向初始空节点
while (P1 && P2) {//当两个多项式都有非零项要处理时
switch (Compare(P1->expon, P2->expon)) {
case 1:
Attach(P1->coef, P1->expon, &rear);
P1 = P1->link;
break;
case -1:
Attach(P2->coef, P2->expon, &rear);
P2 = P2->link;
break;
case 0:
sum = P1->coef + P2->coef;
if (sum) Attach(sum, P1->expon, &rear);
P1 = P1->link;
P2 = P2->link;
break;
}
//将未处理完的另一个多项式的所有节点依次复制到结果多项式中去
for (; P1; P1 = P1->link) Attach(P1->coef, P1->expon, &rear);
for (; P2; P2 = P2->link) Attach(P2->coef, P2->expon, &rear);
rear->link = NULL;
temp = front;
front = front->link;//令front指向结果多项式的第一个非零项
free(temp);//释放临时空表头节点
return front;
}
}
习题
两个有序链表序列的合并
L1
和L2
是给定的带头结点的单链表,其结点存储的数据是递增有序的;函数Merge
要将L1
和L2
合并为一个非递减的整数序列。应直接使用原序列中的结点,返回归并后的带头结点的链表头指针。
输入样例
3
1 3 5
5
2 4 6 8 10
输出样例
1 2 3 4 5 6 8 10
NULL
NULL
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表;空链表将输出NULL */
List Merge( List L1, List L2 );
int main()
{
List L1, L2, L;
L1 = Read();
L2 = Read();
L = Merge(L1, L2);
Print(L);
Print(L1);
Print(L2);
return 0;
}
/* 你的代码将被嵌在这里 */
List Merge(List L1,List L2){
List L = (List)malloc(sizeof(List));//申请新链表
List head = L;//保存头节点
List l1 = L1->Next;//头节点的下一个才开始储存数据
List l2 = L2->Next;
while(l1&&l2){//l1和l2不为空时循环比较,拼接到新链表后面
if(l1->Data <= l2->Data){
L->Next = l1;
l1=l1->Next;
}else{
L->Next = l2;
l2=l2->Next;
}
L = L->Next;
}
if(l1){//l1剩下的节点都拼到新链表后面
L->Next = l1;
}
if(l2){//l2剩下的节点都拼到新链表后面
L->Next=l2;
}
L1->Next = NULL;//拼接完成后原链表指向空
L2->Next = NULL;
return head;
}
一元多项式的乘法与加法运算
题目
输入两个多项式,分成两行
第一位数字是多项式的项数,后面是每一项的系数与指数,用空格分隔。
输出为两行,分别为两个多项式的系数与指数。
输入样例
4 3 4 -5 2 6 1 -2 0
3 5 20 -7 4 3 1
输出样例
15 24 -25 22 30 21 -10 20 -21 8 35 6 -33 5 14 4 -15 3 18 2 -6 1
5 20 -4 4 -5 2 9 1 -2 0
思路
程序的框架应是先读入两个多项式,再调用乘法运算并输出结果,调用加法运算并输出结果。
加法部分在上面有过讲解,不再赘述。
乘法部分可选择把乘法转换为加法:用一个多项式中的某一项乘以另一个多项式,再把结果相加
或者逐项插入:将P1当前项(c1,e1;)乘P2当前项(c2,e2;),并插入到结果多项式中,关键是要找到插入位置。初始结果多项式可由P1第一项乘P2获得
#include<stdio.h>
#include<malloc.h>
typedef struct Node *List;
struct Node{
int coef;//系数
int expon;//指数
List next;
};
List t1,t2;
void Attach(int c,int e,List *l){
List n = (List)malloc(sizeof(struct Node));
n->coef = c;
n->expon = e;
n->next = NULL;
(*l)->next = n;
*l = n;
}
List Read(){
List L,head;
L = (List)malloc(sizeof(struct Node));
L->next = NULL;
head = L;
int n;
scanf("%d",&n);
while(n--){
int c,e;
scanf("%d %d",&c,&e);
Attach(c,e,&L);
}
head = head->next;
return head;
}
List Add(List l1,List l2){
List L,head;
L = (List)malloc(sizeof(struct Node));
L->next = NULL;
head = L;
int sum;
t1=l1;
t2=l2;
while(t1&&t2){
if(t1->expon == t2->expon){
if(t1->coef+t2->coef !=0){
sum = t1->coef+t2->coef;
Attach(sum,t1->expon,&L);
}
t1 = t1->next;
t2 = t2->next;
}
else if(t1->expon > t2->expon){
Attach(t1->coef,t1->expon,&L);
t1=t1->next;
}
else{
Attach(t2->coef,t2->expon,&L);
t2=t2->next;
}
}
if(t1) L->next = t1;
if(t2) L->next = t2;
head = head->next;
return head;
}
List Multiply(List P1,List P2){
List P, Rear, t1, t2, t;
int c, e;
t1 = P1; t2 = P2;
if (!P1 || !P2) return NULL;//如果有一个为NULL,就得返回了。因为没法做乘法。
P = (List)malloc(sizeof(struct Node));
P->next = NULL;
Rear = P;//Rear是指向尾项的指针
while (t2) { // 先用P1的第1项乘以P2,得到P
Attach(t1->coef*t2->coef, t1->expon + t2->expon, &Rear);
t2 = t2->next;
}
t1 = t1->next;
while (t1) {
t2 = P2;
Rear = P;
while (t2) {
e = t1->expon + t2->expon;
c = t1->coef * t2->coef;
//先判断是不是指向NULL
while (Rear->next && Rear->next->expon > e) //遍历链表直到<=当前项
Rear = Rear->next;
if (Rear->next && Rear->next->expon == e) { //链表中存在与当前项相同指数的项,两项合并
if (Rear->next->coef + c)//系数不为零
Rear->next->coef += c;
else {
t = Rear->next;
Rear->next = t->next;
free(t);
}
}
else {//链表中的项比当前项系数小,把当前项插入链表中
t = (List)malloc(sizeof(struct Node));
t->coef = c; t->expon = e;
t->next = Rear->next;
Rear->next = t; Rear = Rear->next;
}
t2 = t2->next;
}
t1 = t1->next;
}
t2 = P; P = P->next; free(t2);
return P;
}
void Print(List l){
if(!l){
printf("0 0");
return;
}
while(l){
printf("%d %d",l->coef,l->expon);
if(l->next) printf(" ");
l=l->next;
}
}
int main(){
List l1,l2,L1,L2;
l1 = Read();
l2 = Read();
L1 = Multiply(l1,l2);
Print(L1);
printf("\n");
L2 = Add(l1,l2);
Print(L2);
return 0;
}