第一章(数据结构和算法)
数据结构
- 官方定义-没有统一
解决问题的方法效率,跟数据的组织方式有关 - 空间的使用
解决问题的方法效率,跟空间的利用率有关 - 算法的效率
解决问题的方法效率,跟算法的巧妙程度有关 - 抽象数据类型
算法
- 定义
1.一个有限指令集
2.接收一些输入
3.产生输出
4.在一定有限步骤后结束
5.每一条指令必须有充分明确的目标,不可以有歧义,在计算机处理范围内,描述不依赖于任何一种计算机语言以及具体的实现手段 - 算法的好坏
时间复杂度S(n)和空间复杂度T(n)
最坏情况复杂度T worst (n)
平均复杂度T avg (n)
Tworst(n)>=Tavg(n - 复杂度分析
复杂度分析的一些小窍门
第二章
线性表
- 多项式表示
- 顺序储存结构直接表示
- 顺序储存结构表示非零项
每个多项式可以看成系数和指数二元组的集合 - 链式结构储存非零项
链表中每个节点储存一个非零项,每个节点包括两个数据域和一个指针域
| coef | expon | link |
typedef struct PolyNode *Polynomial;
struct PolyNode
{
int cofe;
int expon;
Polynomial link;
};
链表的储存形式为:
- 线性表顺序储存
线性表是由同类型数据元素构成有序序列的线性集合
- 表中元素个数成为线性表的长度
- 表中若无元素,称之为空表
- 表中的起始位置称为表头,结束位置称为表尾
线性表的抽象数据类型描述
线性表利用数组连续存储空间的顺序存放线性表的各元素
typedef struct LNode*List;
struct LNode
{
ElementType Data[Maxsize];
int Last;
};
struct LNode L;
List Ptrl;
访问下表为i的元素方法为:L.Data[i]或者PtrL->Data[i]
线性表的长度为L.last+1或者PtrL->Last+1
- 初始化
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;
else return i;
}
//时间复杂度为O(n)
- 顺序储存的插入和删除
- 插入(在i的位置上插入一个值为X的元素(1<=i<=n+1)
先移动再插入
void Insert(ElementType X, int i, List PtrL)
{
int j;
if(PtrL->Last==MAXSIZE-1)
{
printf("该表已满");
return;
}
if(i>=PtrL->Last+2||i<1)
{
printf("位置不合法");
return;
}
for(j = PtrL->Last; j >= i-1; j --)
PtrL->Data[j+1] = PtrL->Data[j];
//将第i个元素插入
//因为下标从0开始所以为i-1
PtrL->Data[i-1] = X;
//因为整体向后移动,要使Last+1继续指向最后一个元素
PtrL->Last++;
}
- 删除操作(删除第i个元素)
后面元素依次向前移动
void Delete(int i ,List PtrL)
{
int j;
//检查i位置是否合法
if(i>=PtrL+2||i<1)
{
printf("不存在第%d个元素",i);
return;
}
for( j = i; j <= PtrL->Last; j ++)
PtrL->Data[j-1]=PtrL->Data[j];
//更新Last
PtrL->Last--;
return;
}
- 链式储存和查找
链式储存解决了线性整体移动的问题,不要求逻辑上相邻的两个元素物理上也相邻,通过链建立数据元素之间的逻辑关系
- 插入删除不需要移动数据元素,只需要修改“链”
typedef struct LNode *List;
struct LNode
{
ElementType Data;
List Next;
};
struct LNode L;
List PtrL;
//在这里注意与顺序存储的区别
- 求表长
int Length(List PtrL)
{
//令p指向表的第一个节点
List p = PtrL;
int j;
while(j)
{
p = p->Next;
j++;
}
return j;
//返回的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 i;
else return NULL;
}
- 按值查找
List Find( ElementType X, List PtrL)
{
List p = PtrL;
while(p!=NULL&&p->Next!=X)
p = p->Next;
return p;
}
- 链式储存的插入和删除
- 插入操作(在第i个结点位置上插入一个值为X的新结点,换句话说就是在第i-1个结点后插入一个值为X的新节点)
- 构造一个新结点用s指
- 找到链表的第i-1个结点,用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;
}
//此处查找的节点为i-1
p = Findkth(i-1,PtrL);
//该结点不存在
if(p==NULL)
{
printf("参数出错");
return NULL;
}
//申请新的结点
else
{
s=(List)malloc(sizeof(struct LNode));
s->Data = X;
s->Next = p->Next;
p->Next = s;
return PtrL;
}
//将第i个结点插在i-1个结点的后面
- 删除操作(删除第i个结点)(1<=i<=n)
- 先找到第i-1个结点,用p指向
- 然后用s指针指向第i个结点,即为p结点的下一个结点
- 修改指针,删除s所指向的结点
- 最后释放s所指向结点的空间free
List Delete(int i,List PtrL)
{
List p,s;
//删除结点分为删除头结点和第i个结点
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个结点和i个结点
if(p==NULL)
{
printf("第%d个结点不存在",i-1);
return NULL:
}
else if (p->Next==NULL)
{
printf("第%d个结点不存在",i);
return NULL;
}
else
{
s=p->Next;//s指向i
p->Next=s->Next;//删除操作
free(s);
return PtrL;
}
-
广义表和多重链表
一元多项式可以用上述式子表示,二元多项式又该如何表示?可以用“复杂”链表表示
- 广义表是线性表的推广
- 对于线性表来说,n个元素都是基本的单元素
- 广义表中,这些元素不仅是单元素也可以是另一个广义表
typedef struct GNode*GList;
struct GNode
{
int Tag;//标志域,0表示结点是单元素,1表示结点是广义表
union
{
ElementType Data;
GList Sublist;
}URegion;
//这里数据域Data(单元素)和指针域Sublist复用,共用存储空间
GList Next;//指向后继结点
};
-
多重链表
多重链表中的结点属于多个链多重链表中的结点指针域有很多, 但是包含两个指针域的链表并不一定是多重链表, 比如双向链表不是多重链表 多重链表可以用在树和图中实现存储
堆栈
- 什么是堆栈
中缀表达式:运算符位于两个数字之间
后缀表达式:运算符位于两个数字之后
堆栈是具有一定操作约束的线性表,只在一端做插入,删除
Last In First Out(LIFO)后入先出
抽象数据类型
原理图
- 堆栈的顺序存储
栈的顺序存储结构通常是由一个一维数组和一个记录栈顶元素位置的变量组成#define Maxsize<存储数据元素的最大值> typedef struct SNode *Stack struct SNode { ElementType Data[Maxsize]; int Top; };
- 入栈
void Push(Stack PtrL,ElementType item)
{
if(PtrL->Top==Maxsize-1)
{
printf("堆栈满");
return;
}
else
{
PtrL->Data[++(PtrL->Top)] = item;
return;
}
}
- 出栈
ElementType Pop(Stack PtrL)
{
if(PtrL->Top==-1)
{
printf("堆栈空");
return ERROR;
}
else
return (PtrL->Data[(PtrL->Top)--]);
}
-
用数组实现两个堆栈,最大利用数组空间,若有空间则可以实现入栈
两个栈分别从数组的两头开始向中间生长,当两个栈的栈顶指针相遇时,表示两个栈都已经满
#define MaxSize <存储数据元素的最大个数>
struct DoubleStack{
ElementType Data[MaxSize];
int Top1;
int Top2;
//其中top1和top2两个指针
}S;
S.Top1=-1;
S.Top2=MaxSize;
}
void Push(struct DoubleStack *PtrS,ElementType item,int Tag)
{
if(PtrS->Top2-PtrS->Top1 == 1)
{
printf("堆栈满");
return;
}
if(Tag==1)
PtrS->Data[++(PtrS->Top1)] = item;
else
PtrS->Data[--(PtrS->Top2)] =item;
}
ElementType Pop(struct DoubleStack *PtrS, int Tag)
{
if(Tag==1)
{
if(PtrS->Top1 == -1)
{
printf("堆栈1空");
return NULL;
}
else return PtrS->Data[(PtrS->Top1)--];
}
else
{
if(PtrS->Top2 == Maxsize)
{
printf("堆栈空");
return NULL;
}
else return PtrS->Data[(PtrS->Top2)++];
}
}
- 堆栈的链式存储
- 链式存储结构实际上就是一个单链表,叫做链栈,插入和删除操作只能在链栈的栈顶进行
typedef struct SNode *Stack;
struct SNode
{
ElementType Data;
struct SNode *Next;
};
- 初始化
Stack CreatStack()
{
Stack s;
s = (Stack)malloc (sizeof(struct SNode));
s->Next = NULL;
return s;
}
- 判断堆栈s是否为空
int Empty(Stack s)
{
return (s->Next == NULL);
}
- 入栈
void Push(ElementType item,Stack s)
{
struct SNode *Temcell;
Tmpcell = (struct *SNode)malloc(sizeof (struct SNode));
Tmpcell->Element = item;//赋值
Tmpcell->Next = s->Next;
s->Next = Tmpcell;
}
- 出栈
ElementType Pop(Stack s)
{
struct SNode *Firstcell;
ElementType TopElement;
if(Empty(s))
{
printf("堆栈空");
return NULL;
}
else
{
//Firstcell是删除的元素,TopElement是栈顶元素
Firstcell = s-Next;
s->Next = Firstcell->Next;
TopElement = Firstcell->Element;
free(Firstcell);
return TopElement;
}
}
- 堆栈的应用
中缀表达式转换为后缀表达式
- 运算数:直接输出
- 左括号:入栈
- 右括号:栈顶元素出栈并输出,直到遇到左括号
- 运算符:优先级大于栈顶运算符,入栈;优先级小于等于栈顶运算符出栈输出,继续比较新的栈顶运算符
- 处理完毕后,将堆栈剩余元素一并输出
队列
- 队列及顺序存储
具有一定操作约束的线性表
-
插入和删除操作:只能在一端插入,而在另一端删除
数据插入:入队列
数据删除:出队列
先进先出(FIFO)First In First Out -
抽象数据描述
-
队列存储的实现
队列的顺序存储结构通常由一个一维数组和一个记录队列头元素位置的变量ront以及一个记录队列尾元素位置的变量rear组成
#define Maxsize<数据元素的最大个数>
struct QNode{
ElementType Data[Maxsize];
int rear;
int front;
};
typedef struct QNode *Queue;
//front 指的是第一个元素之前的位置
在顺环队列判断是否满的问题上使用额外标记Tag域或者Size
- 入队
void AddQ(Queue PtrL,ElementType item)
{
if(PtrQ->rear+1)%Maxsize ==PtrQ->front)
{
printf("队列满");
return;
}
PtrQ->rear = (PtrQ->rear+1)%Maxsize;
PtrQ->Data[PtrQ->rear]=item;
}
- 出队
ElementType DQ(Queue PtrQ)
{
if(PtrQ->rear==PtrQ->front)
{
printf("队列空");
return ERROR;
}
else
{
PtrQ->front = (PtrQ->front+1)%Maxsize;
return PtrQ->Data[PtrQ->front];
}
}
- 队列的链式存储
struct Node{
ElementType Data;
struct Node *Next;
};
struct QNode{
struct Node *rear;
struct Node *front;
};
typedef struct QNode *Queue;
Queue PtrQ;
- 出队
ElementType DQ(Queue PtrQ)
{
struct Node *Frontcell;
ElementType Frontelement;
if(PtrQ->front==NULL)
{
printf("空");
return ERROR;
}
Frontcell = PtrQ->front;
//分情况讨论,队列只有一个元素和多个元素
if(PtrQ->front == PtrQ->rear)
PtrQ->front=PtrQ->rear=NULL;
else
PtrQ->front = PtrQ->front->Next;
Frontelement = Frontcell->Data;
free(Frontcell);
return Frontelement;
}
多项式问题
- 加法运算的实现
采用不带头结点的单项链表。按照指数递减的顺序排列各项
struct PolyNode{
int coef;//系数
int expon;//指数
struct PolyNode *link;
};
typedef struct PolyNode *Polynomial;
Polynomial P1,P2;
- 算法思路
- P1->expon==P2->expon:系数相加,若结果不为0,则作为结果多项式对应系数。同时,P1和P2 都指向下一项
- P1->expon>P2->expon将P1存入当前多项式,并使P1指向下一项
- P1->expon < P2->expon将P2存入当前多项式,并使P2指向下一项
Polynomial Polyadd(Polynomial P1,Polynomial P2)
{
Polynomial front ,rear,temp;
int sum;
rear = (Polynomial) malloc (sizeof (struct PolyNode);
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,P1->link,&rear);
for(;P2;P2=P2->link)Attach(P2,P2->link,&rear);
rear->link = NULL;
temp = front;
front = front->link;
free(temp);
return front;
}
}
void Attach( int c, int e, int Polynomial *pRear)
{
Polynomial P;
P = (Polynomial)malloc (sizeof (struct PolyNode));
P->coef = c;
P->expon = e;
p->link = NULL:
(*pRear)->link = P;
*pRear = P;
//修改pRear值
}
- 题意理解与多项式表示
数据结构设计typedef struct PolyNode *Polynomial; struct PolyNode { int coef; int expon; Polynomial link; };
- 程序框架及读入多项式
//ReadPoly()读入一个多项式
//Mult()两个多项式相乘
//Add()两个多项式相加
//PrintPoly()打印一个多项式
int main()
{
Polynomial P1,P2,PP,PS;
P1 = ReadPoly();
P2 = ReadPoly();
PP = Mult(P1,P2);
PrintPoly(PP);
PS = Add(P1,P2);
PrintPoly(PS);
return;
}
读取多项式
Polynomial ReadPoly()
{
Polynomial P,Rear,t;
int c,e,N;
scanf("%d",&N);
//链表头空结点
P = (Polynomial)malloc(sizeof(struct PolyNode));
P->link = NULL:
Rear = P;
while(N--)