教材:数据结构(第二版)主编:陈越
线性结构:
所谓线性结构就是数据像一条线一样串在一起,是一个抽象的概念,其中每一个元素都是直接前驱和直接后驱,数据的存储分为逻辑结构和物理结构,物理结构包括了顺序存储(在内存中用地址连续的一块存储空间顺序存放数据)和链式存储(不需要连续的存储空间),逻辑结构包括了诸如线性表,树,集合,图等,而常见的数组其实是线性表的一种实现,但是线性表和数组之间没有从属关系。线性表的典型应用就是堆栈和队列;顺序存储和逻辑存储的优缺点(原帖地址)
本章讲解了:线性表(List)的顺序存储(顺序表)和链式存储(单链表,双向链表,广义表,多重链表),堆栈(Stack)的顺序存储(双堆栈)和链式存储,队列(queue)的顺序存储和链式存储(循环队列)
线性表:
主要操作集:
1.初始化生成:List MakeEmpty();
2.按位序查找: ElementType FindKth(List L, int i );
3.按数值查找: Position Find(List L, ElementType X); //typedef int Position;
4.插入: bool Insert(List L, ElementType X, int i);
5.删除: bool Delete(List L, int i);
6.返回长度:int Length(List L);
线性表的顺序存储:(顺序表)
顺序表的结构:
typedef enum{true, false} bool;
#define ERROR -1
typedef struct LNode
{
ElementType Data[MAXSIZE];
Position Last;//保存最后一位的位置
}*PtrToNode;
typedef PtrToNode List;
初始化:
List MakeEmpty( )
{
List L;
L = (List)malloc(sizeof(struct LNode));
L->Last = -1;
return L;
}
查抄:
ElementType FindKth(List L, ElementType X )
{
Position i =0;
while(i<=Last&&L->Data[i]!=x)
{
i++;
}
if(i>Last)return ERROR;
else return i;
}
插入:
bool Insert(List L, ElementType X, int i )
{
Position m = L->Last;
//表是否已满
if(L->Last==MAXSIZE-1)
{
printf("表满");
return false;
}
//插入位置合法性
if(i<1||i>L->Last2)
{
printf("输入位置非法");
return false;
}
while(m>=i-1)
{
L->Data[m+1] = L->Data[m];
m--;
}
L->Data[i-1] = X;
L->Last++;
return true;
}
在等概率条件下,平均移动元素的次数为(n/2)
该方法的时间复杂度为 O(n)
删除:
bool Delete(List L, int i)
{
Position m = i ;
if(L->Last == -1)
{
printf("空表");
return false;
}
if(i<1||i>L->Last+1)//其实可以把判断空表的条件和这个合并,因为当Last==1时,i如果不大于0,就会返回这是非法操作,但是i必须是大于0的
{
printf("输入位置非法");
return false
}
while(m>=L->Last)
{
L->Data[m] = L->Data[m+1];
m++;
}
L->Last--;
return true;
}
在等概率条件下,平均移动元素的次数为((n-1)/2)
该方法的时间复杂度为 O(n)
线性表的链式存储:
(单链表)
单链表的结构:
typedef struct LNode
{
ElementType Data;
PtrToNode Next;
}*PtrToNode;
typedef PtrToNode List;
typedef PteToNode Position; //结点的地址
求表长:
int Length(List L)
{
int count = 0;
Position P;
P = L;//无头结构的链表L,则P指向第一个结点
while(P)
{
P=P->Next;
count++;
}
return count;
}
查找:
按序号查找:
#define ERROR -1
ElementType FindKth(List L, int i)
{
Position P = L;
int count = 1;
while(P && count<i)
{
P = P->Next;
count++;
}
if((count == i) && P)return P->Data;
else return ERROR;
}
按值查找:
#define ERROR NULL
Position Find(List L,ElementType X)
{
Position P = L;
while(P&&(P->Data!=X)) P = P->Next;
if(P)return P;
else return ERROR;
}
上述两种查找方法的时间复杂度均为 O(N)
插入:(注意这里最好增加一个空的头节点,当出现插入在表头的特殊情况的时候,因为表头指针指向的值会因为添加和删除发生变化,假如插入失败,返回值为NULL,L = Insert(L,X,i)是不可以的,所以解决办法要么增加头节点,返回值改为返回bool类型,要么函数参数为List *L,即采用双指针的形式)
//假设这里给出的L是带头节点的链表
bool Insert(List L, ElementType X, int i )
{
Position tempNode,P;
int count = 0;
P = L;//P指向表头,如果P指向第一结点,那么不能检测插入在第一个节点之前的情况
while(P && count<i-1)//找到待插入序列的前一个序列
{
P = P->Next;
count++;
}
if(P==NULL||count!=i)
{
printf("输入参数非法");
return false;
}else
{
tempNode = (Position)malloc(sizeof(struct LNode));
tempNode->Data = X;
tempNode->Next = P->Next;
P->Next = tempNode;
return true;
}
}
删除:
bool Delete(List L, int i)
{
Position P,tempNode;
P = L;
int count = 0;
while(P && count<i-1)
{
P = P->Next;
count++;
}
if(P == NULL || count!=i-1 || P->Next == NULL)
{
printf("输入非法");
return false;
}else
{
tempNode = P->Next;
P->Next = tempNode->Next;
free(tempNode);
return true;
}
}
广义表和多重链表:
广义表采用链式存储的方法,用到了联合体(union),一般来说会有两个指针域,多重链表每个结点有多个指针域,但是包含两个指针域的链表并不一定是多重链表,多重链表多用在例如树,图这样复杂的数据结构。
例如,稀疏矩阵的数据结构:
typedef enum{Head,Term}NodeTag;
struct TermNode
{
int Row,Col;
ElementType Value;
};
typedef struct MNode * PtrToNode;
struct MNode
{
PtrToNode Down,Right;
NodeTag Tag;
union
{
PtrToNode Next;
struct TermNode Term;
}URegion;
};
typedef PtrToMNode Matrix;
Matrix HeadNode[MAXSIZE];
堆栈
了解中缀表达式转前缀表达式和后缀表达式的过程:
转后缀表达式:从左到右看,遇到数字输出,遇到符号进栈,括号要匹配,当进栈的符号优先级小于栈顶符号,将栈内符号输出一直到栈顶符号优先级小于等于进栈的符号,入栈,以此类推,最后采用LIFO(Last In First Out)的原则输出
转前缀表达式:从右到左看,遇到数组输出,遇到符号进栈,括号要匹配,进栈的符号优先级小于栈顶符号,将栈内符号输出一直到栈顶符号优先级小于等于进栈的符号,入栈,以此类推,最后采用LIFO(Last In First Out)的原则输出
堆栈的操作集:
1.生成:Stack CreateStack(int MaxSize);
2.是否满栈: bool IsFull(Stack S);
3.入栈: bool Push(Stack S, ElementType X);
4.是否空栈:bool IsEmpty(Stack S);
5.出栈:ElementType Pop(Stack S);
堆栈的顺序存储实现:
typedef int Position;
typedef struct SNode
{
ElementType* Data;
Position Top;
int MaxSize;
}*Stack;
入栈:
bool IsFull(Stack S)
{
return(S->Top == MaxSize-1);
}
bool Push(Stack S, ElementType X)
{
if(IsFull(S))
{
printf("堆栈满");
return false;
}
S->Data[++(S->Top)] = X;//注意不要写成(S->Top)++
return true;
}
出栈:
#define ERROR -1
bool IsEmpty(Stack S)
{
return(S->Top == -1);
}
ElementType Pop(Stack S)
{
if(IsEmpty(S))
{
printf("栈空");
return ERROR;
}
return(S->Data[(S->Top)--]);
}
堆栈的链式存储:
同样有一个top的指标用来指示栈顶,但是这里的top指针就是头指针,每一次的出栈和入栈都是从头指针这里开始,这里我们使用带头节点的链表
typedef Stack PtrToNode;
typedef struct SNode
{
ElementType Data;
PtrToNode Next;
}*Stack;
bool IsEmpty(Stack S)
{
return(S->Next == NULL);
}
bool Push(Stack S, ElementType X)
{
PtrToNode tempNode;
PtrToNode P = S;
tempNode = (PtrToNode)malloc(sizeof(struct SNode));
tempNode ->Data = X;
tempNode ->Next = P->Next;
P->Next = tempNode;
return true;
}
ElementType Pop(Stack S)
{
if(IsEmpty(S))
{
printf("栈空");
return false;
}
PtrToNode tempNode;
tempNode = S->Next;
ElementType topValue;
topValue = tempNode->Data;
S->Next = tempNode->Next;
free(tempNode);
return topValue;
}
双堆栈的顺序存储:
typedef int Position;
typedef struct SNode
{
ElementType * Data;
Position Top1,Top2;
int Maxsize;
}* PtrToNode;
typedef PtrToNode Stack;
//Tag作为指标,指示插入第一个栈还是第二个栈,两栈满的条件是Top1,Top2相遇
bool Push(Stack S,ElementType X, int Tag)
{
if(S->Top1 == (S->Top2-1))
{
printf("堆栈满");
return false;
}
if(Tag == 1) S->Data[++(S->Top1)]=X;
else if(Tag == 2) S->Data[++(S->Top2)]=X;
return true;
}
ElementType Pop(Stack S, int Tag)
{
if(Tag == 1)
{
if(S->Top1 == -1) printf("该堆栈空");
return ERROR;
}else return S->Data[(S->Top1)--];
}else if(Tag == 2)
{
if(S->Top2 == S->Maxsize) printf("该栈为空");
return ERROR;
}else return S->Data[(S->Top2)--];
}
}
队列
队列的顺序存储:
插入和删除:
typedef int Position;
typedef struct QNode
{
ElementType * Data;
Position Front,Rear;
int Maxsize;
}*PtrToNode;
typedef PtrToNode Queue;
Queue CreateQueue(int Maxsize)
{
Queue Q = (Queue)malloc(sizeof(struct QNode));
Q->Data=(ElementType*)malloc(Maxsize*sizeof(ElementType));
Q->Maxsize = Maxsize;
Q->Front = Q->Rear = 0;
return Q;
}
bool IsFull(Queue Q)
{
return((Q->Rear+1)%Q->Maxsize == Q->Front);
}
bool IsEmpty(Queue Q)
{
return (Q->Rear == Q->Front);
}
bool AddQ(Queue Q, ElementType X)
{
if(IsFull(Q))
{
printf("栈满");
return false;
}else
{
Q->Rear = (Q->Rear + 1)%Q->Maxsize;
Q->Data[Q->Rear] = X;
return true;
}
}
Elementype DeletedQ(Queue Q)
{
if(IsEmpty(Q))
{
printf("队列空");
return ERROR;
}else
{
Q->Front = (Q->Front + 1)%Q->Maxsize;
return Q->Data[Q->Front];
}
}
队列的链式存储:
(不带头节点)
typedef struct Node
{
ElementType Data;
PtrToNode Next;
}*PtrToNode;
typedef PtrToNode Position;
typedef struct QNode
{
Position Front,Rear;
int Maxsize;
}*PtrToNode;
typedef PtrToNode Queue;
bool IsEmpty(Queue Q)
{
return(Q->Front== NULL);
}
ElementType DeleteQ(Queue Q)
{
Position P;
ElementType value;
if(IsEmpty(Q))return ERROR;
else
{
P = Q->Front;
if(Q->Front == Q->Rear) Q->Front = Q-Rear =NULL;
else Q->Front = Q->Front->Next;
value = P->Data;
free(P);
return value;
}
}