数据结构重点知识复习

鱼天翱 2019-07-01

数据结构

数据对象在计算机中的组织方式
逻辑结构(线性结构,树结构,图结构)
物理结构
数据对象必定与一系列加在其上的操作相关联
完成这些操作所用的方法就是算法

抽象数据类型(Abstract Data Type)

数据类型

数据对象集
数据集合相关联的操作集

抽象:描述数据类型的方法不依赖于具体实现

与存放的机器无关
与数据存储的物理结构无关
与实现操作的算法和编程语言无关

算法(Algorithm)

一个有限指令集
接受一些输入(或没有输入)
产生输出
在有限步骤之后终止
每一条指令必须

有充分明确的目标,不可有歧义
在计算机能处理的范围之内
描述应不依赖于任何一种计算机语言以及其具体的实现手段

什么是好算法?

空间复杂度S(n)——占用存储单元的长度
时间复杂度T(n)——耗费时间的长度

一般考虑最坏情况复杂度Tworst(n)和平均复杂度Tavg(n),Ω(g(n)) <= T(n) <= O(f(n))

复杂度排序:常数<logn<n<nlogn<n2<n3<2n<n!

例子:给定N个整数的序列{A1, A2, …, AN},求函数f(i, j) = max{0, ∑_(k=i)^j▒A_k }的最大值

T(n) = O(N3)

int MaxSubseqSum1(int A[], int N){

int ThisSum, MaxSum = 0;
int i, j, k;
for(i = 0; i < N; i++){
    for(j = i; j < N; j++){
        ThisSum = 0;
        for(k = i; k <= j; k++) ThisSum += A[k];
        if(ThisSum > MaxSum) MaxSum = ThisSum;
    }
}
return MaxSum;

}

T(n) = O(N2)

int MaxSubseqSum2(int A[], int N){

int ThisSum, MaxSum = 0;
int i, j;
for(i = 0; i < N; i++){
    ThisSum = 0;
    for(j = i; j < N; j++){
        ThisSum += A[j];
        if(ThisSum > MaxSum) MaxSum = ThisSum;
    }
}
return MaxSum;

}

T(n) = O(NlogN)

分而治之,算左边的最大、右边最大和跨左右最大。
这时候T(N) = 2T(N/2) + cN,其中T(1) = O(1),将N/2代入:
T(N) = 2[2T(N/22) + cN/2] + cN

=2kO(1) + ckN

其中N/2k = 1
所以复杂度主导因子为ckN,即O(NlogN)

T(n) = O(N),在线处理

int MaxSubseqSum4(int A[], int N){

int ThisSum, MaxSum = 0;
int i, j;
for(i = 0; i < N; i++){
    ThisSum += A[i];
    if(ThisSum > MaxSum) MaxSum = ThisSum;
    else if(ThisSum < 0) ThisSum = 0;
}
return MaxSum;

}

线性结构
一元多项式及其运算
F(x) = a0 + a1X + … + an-1Xn-1 + anXn 运算考虑加减乘

顺序存储结构直接表示

a[i]:项Xi的系数为ai
如f(x) = 4X5 – 3X2 + 1, 表示成{1, 0, -3, 0, 0, 4}

顺序存储结构表示非零项

f(x) = 9x12 + 15X8 + 3X2,表示成{{9, 12}, {15, 8}, {3, 2}}

链表结构存储非零项

coef expon link
typedef struct PolyNode *Polynomial;
struct PolyNode{

int coef;
int expon;
Polynomial link;

}

线性表(Linear List):
由同类数据元素构成有序序列的线性结构

表中元素个数称为线性表长度
没有元素时称为空表
起始位置为表头,结束位置为表尾

线性表的抽象数据类型描述:
类型名称:线性表
数据对象集:是n(n>=0)个元素构成的有序序列(a1, a2, …, an)
操作集:线性表L∈List,整数i表示位置,元素X∈ElementType

List MakeEmpty()
ElementType FindKth(int K, List L)
int Find(ElementType X, List L)
void Insert(ElementType X, int i, List L)
void Delete(int i, List L)
int Length(List L)
以数组方式实现:

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;

}

插入

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--) PtrL->Data[j+1] = PtrL->Data[j];
PtrL->Data[i-1] = X;
PtrL->Last++;
return;

}

删除

void Delete(int i, List PtrL){

int j;
if(i < 1 || i > PtrL->Last + 1){
    printf("不存在第%d个元素", i);
    return;
}
for(j = 1; j <= PtrL->Last; j++) PtrL->Data[j-1] = PtrL->Data[j];
PtrL->Last--;
return;

}

以链表方式实现:

typedef struct LNode *List;
struct LNode{

ElementType Data;
List Next;

};
struct LNode L;
List PtrL;

求表长

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);
if(p == NULL){
    printf("参数i错");
    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);
if(p == NULL){
    printf("第%d个结点不存在", i-1);
    return NULL;
}else if(p->Next == NULL){
    printf("第%d个结点不存在", i);
    return NULL;
}else{
    s = p->Next;
    p->Next = s->Next;
    free(s);
    return PtrL;
}

}

广义表:
二元多项式P(x, y) = 9x12y2 + 4x12 + 15x8y3 – x8y + 3x2
看成关于x的一元多项式:
P(x, y) = (9y2 + 4)x12 + (15y3 - y)x8 + 3x2
通过复杂链表表示
广义表(Generalized List)

广义表是线性表的推广
对于线性表而言,n个元素都是基本的单元素
广义表中,这些元素不仅可以是单元素也可以是另一个广义表

typedef struct GNode *GList;
struct GNode{

int Tag; /*标志域,0表示节点是单元素,1表示节点是广义表*/
union{  /*子表指针域SubList与单元素数据域Data复用,即公用存储空间*/
    ElementType Data;
    GList SubList;
}URegion;
GList Next;

};
Tag Data Next

SubList

多重链表:链表中的节点可能同时隶属于多个链

多重链表中节点的指针域会有多个,如前面例子包含了Next和SubList两个指针域;
但包含两个指针域的链表并不一定是多重链表,比如双向链表不是多重链表

多重链表有广泛的用途,基本上如树、图这样相对复杂的数据结构都可以采用多重链表方式实现存储。
矩阵可以用二维数组表示,但有两个缺陷,一是数组的大小需要事先确定,二是对于稀疏矩阵将造成大量的存储空间浪费。
可以采用一种典型的多重链表——十字链表来存储稀疏矩阵:

只存储矩阵非0元素项

节点的数据域:行坐标Row、列坐标Col、数值Value

每个节点通过两个指针域,把同行、同列串起来;

行指针Right
列指针Down

4行5列7个元素

用一个标识域Tag来区分头节点和非0元素节点;
头节点的标识值为“Head”,矩阵非0元素节点的标识值为“Term”。

Tag
Down URegion Right

节点的总体结构

Term
Down Row Col Right

Value    
矩阵非0元素节点

Head
Down Next Right

头节点

堆栈
计算机是如何进行表达式计算的?
首先将中缀表达式(a+bc-d/e)转换为后缀表达式(abc+de/-)
如62/3-42+=6/2-3+42=8:从左向右扫描,遇到运算数就记住,遇到运算符号就将最近记住的两个数进行计算,就需要先进后出,即堆栈。
定义:Stack,具有一定操作约束的线性表,只在一端(栈顶,Top)做入栈(Push)和出栈(Pop),遵循后入先出(Last In First Out,LIFO)
抽象数据类型描述
类型名称:堆栈
数据对象集:一个有0个或多个元素的有穷线性表。
操作集:长度为MaxSize的堆栈S∈Stack,堆栈元素item∈ElementType

生成空堆栈,最大长度为MaxSize
判断堆栈S是否已满
将元素item压入堆栈
判断堆栈S是否为空
删除并返回栈顶元素

栈的顺序存储实现
typedef ElementType;
typedef struct SNode *Stack;
struct SNode{

ElementType Data[MaxSize];
int Top;

}
入栈Push
void Push(Stack PtrS, ElementType item){

if(PtrS->Top == MaxSize-1){
    printf("fullstack");
    return;
} else{
    PtrS->Data[++(PtrS->Top)] = item;
    return;
}

}
出栈Pop
ElementType Pop(Stack PtrS){

if(PtrS->Top == -1){
    printf("emptystack");
    return ERROR;
} else{
    return PtrS->Data[PtrS->Top--];
}

}

栈的链式存储实现
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(Stack S, ElementType item){

struct SNode *TempCell;
TempCell = (Stack)malloc(sizeof(struct SNode));
TempCell->Data = item;
TempCell->Next = S->Next;
S->Next = TempCell;

}
出栈
ElementType Pop(Stack S){

struct SNode *FirstCell;
ElementType TopElem;
if(IsEmpty(S)){
    printf("emptystack");
    return NULL;
}else{
    FirstCell = S->Next;
    S->Next = FirstCell->Next;
    TopElem = FirstCell->Data;
    free(FirstCell);
    return TopElem;
}

}

堆栈应用:表达式求值
中缀表达式转换为后缀表达式再求值

运算数相对顺序不变
运算符号顺序发生改变

需要存储“等待中”的运算符号
要将当前运算符号与“等待中”的最后一个运算符号比较
例子:a(b+c)/d=abc+d/
流程:从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。

运算数:直接输出;
左括号:压入堆栈;
右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
运算符:
若优先级大于栈顶运算符时,则把它压栈;
若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
若各对象处理完毕,则把堆栈中存留的运算符一并输出。

例子:(2*(9+6/3-5)+4)
步骤 待处理表达式 堆栈状态
(底顶) 输出状态
1 2*(9+6/3-5)+4
2 *(9+6/3-5)+4 2
3 (9+6/3-5)+4 * 2
4 9+6/3-5)+4 *( 2
5 +6/3-5)+4 *( 29
6 6/3-5)+4 *(+ 29
7 /3-5)+4 *(+ 296
8 3-5)+4 *(+/ 296
9 -5)+4 *(+/ 2963
10 5)+4 *(- 2963/+
11 )+4 *(- 2963/+5
12 +4 * 2963/+5-
13 4 + 2963/+5-*
14 + 2963/+5-*4
15 2963/+5-*4+
堆栈其他应用:
函数调用及递归实现
深度优先搜索
回溯算法…

队列
遵循先入先出(First In First Out,FIFO)
队列的抽象数据类型描述
类型名称:队列
数据操作集:一个有0个或多个元素的有穷线性表。
操作集:长度为MaxSize的队列Q∈Queue,队列元素item∈ElementType

生成长度为MaxSize的空队列
判断队列Q是否已满
将数据元素item插入队列Q中
判断队列Q是否为空
将队头数据元素从队列中删除并返回

队列的顺序存储实现
typedef 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("fullqueue");
    return;
}
PtrQ->rear = (PtrQ->rear+1)%MaxSize;
PtrQ->Data[PtrQ->rear] = item;

}
出队列
ElementType DeleteQ(Queue PtrQ){

if(PtrQ->front == PtrQ->rear){
    printf("emptyqueue");
    return NULL;
}else{
    PtrQ->front = (PtrQ->front+1)%MaxSize;
    return PtrQ->Data[PtrQ->front];
}

}

队列的链式存储实现
typedef ElementType;
struct Node{

ElementType Data;
struct Node *Next;

};
struct QNode{

struct Node *rear;
struct Node *front;

};
typedef struct QNode *Queue;
Queue PtrQ;
出队
ElementType DeleteQ(Queue PtrQ){

struct Node *FrontCell;
ElementType FrontElem;

if(PtrQ->front == NULL){
    printf("emptyqueue");
    return ERROR;
}
FrontCell = PtrQ->front;
if(PtrQ->front == PtrQ->rear)
    PtrQ->front = PtrQ->rear = NULL;
else
    PtrQ->front = PtrQ->front->Next;
FrontElem = FrontCell->Data;
free(FrontCell);
return FrontElem;

}

应用实例:多项式加法运算

相关推荐