数据结构--2--stack, heap, queue, tree

//堆栈 stack  一个有0个或多个元素的又穷线性表
//长度为MaxSize 的堆栈
Stack CreateStack(int MaxSize); //生成空栈表,最大MaxSize
int IsFull(Stack S, int MaxSize); //判断堆栈S是否已满
void Push(Stack S, ElementType item); //将元素item压入堆栈
int IsEmpty(Stack S); //判断堆栈S 是否为空
ElementType Pop(Stack S); //删除并返回栈顶元素

//堆栈的顺序存储实现 -------------------------------------------------------------------------------------------------第一部分:堆栈
#define MaxSize <存储数据元素的最大个数>
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->Pop == -1){
        printf("堆栈空");
        return ERROR; //ERROR是elementType的特殊值,标志错误
    }
    else
    {
        return(PtrS->Data[(PtrS->Top)--]);
    } 
}

//堆栈可用于函数调用及递归实现,深度优先搜索,回溯算法等。
//-------------------------------------------------------------------------------------------- 一个数组实现两个堆栈
//一个数组实现两个栈: 从数组两头开始向中间生长;
//当两个栈的栈顶指针相遇时,表示两个栈都满了
#define MaxSize<存储数据元素的最大个数>
struct DStack{ ElementType Data[MaxSize]; int Top1; //堆栈1的栈顶指针
    int Top2; //堆栈2的栈顶指针
}S;
S.Top1 = -1; //此时空
S.Top2 = MaxSize; //此时空

//push入栈
void Push(struct DStack *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;
}
//pop出栈
ElementType Pop( struck DStack *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("堆栈2空");
            return NULL;
        }
        else return PtrS->Data[(PtrS->Top2)++];
    }
}
//----------------------------------------------------------------------------------------------------------链表实现堆栈
//stack 的链式存储-》单链表,叫链栈
//插入和删除操作只能在栈顶进行
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;
}

//判断堆栈S是否为空,若空返回1,否则返回0
int IsEmpty(Stack S)
{
    return (S->Next == NULL);
}

//将item压入堆栈S
void Push (ElementType item, Stack S)
{
    struct SNode *TmpCell;
    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 Topelem;
    if(IsEmpty(S)){
        printf("堆栈空");
        return NULL;
    }
    else{
        FirstCell = S->Next;
        S->Next = FirstCell->Next;
        TopElem = FirstCell->Element;
        free(FirstCell);
        return TopElem;
    }
   
}
//-------------------------------------------------------------------------------堆栈是后进先出,队列是先进先出

//---------------------------------------------------------------------------------------------------------------第二部分:队列
//队列,具有一定操作约束的线性表,只能在一端插入,而在另一端删除,先进先出
//顺序实现队列 #define MaxSize <存储数据元素的最大个数> struct QNode{ ElementType Data[MaxSize]; int rear; int front; }; typedef struct QNode *Queue; //判断环队列是否空或满,可只用前MaxSize-1, 或使用tag或Size标识。
//-------------------------------------------------------------------队列结构的操作 //第一种方式,入队列 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]; } } //--------------------------------------------------------------------------------链表实现队列 //单向链表实现队列,链表尾不能做删除 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("队列空"); 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; }
//查找
//静态查找:集合中记录固定,没有插入和删除,只有查找
//动态查找:集合中记录是动态变化的,除查找还可能发生插入和删除
//-----------------------------------------------------------------------------------------------------第三部分:查找操作的讨论,二叉树

//------------------------------------------------------------------------------------------查找操作 //顺序查找的实现 int SequentialSearch (List Tb1, ElementType K) { //在Element[1]~[n]中查找关键字为K的数据元素 int i; for(i = Tb1->Length; i>0&& Tb1->Element[i] != k;i--); return i; //查找成功返回下标,不成功返回i } typedef struct LNode *List; struct LNode{ ElementType Element[MAXSIZE]; int Length; }; //顺序查找的一种实现:哨兵 //在边界放一个哨兵 int SequentialSearch (List Tb1, ElementType K) { int i; Tb1->Element[0] = K; //建立哨兵 for(i = Tb1->Length; Tb1->Element[i]!= K;i--) return i; //查找成功返回下标,不成功返回0 } //---------------------------------------------------------------------------------------二分查找 //二分查找:先决条件:有序、连续存放(数组) int BinarySeach(List Tb1, ElementType K) { int left, right, mid, NoFound = -1; left = 1; //初始左边界 right = Tb1->Length; //初始右边界 while(left <= right) { mid = (left+right)/2; //计算中间元素坐标 if(K<Tb1->Element[mid]) right = mid-1; //调整右边界 else if (K>Tb1->Element[mid]) left = mid+1; //调整左边界 else return mid; //查找成功,返回数据元素下标 } return NotFound; //查找不成功,返回-1 } //--------------------------------------------------------------------------------判定树(从二分查找衍生)->查找的次数是该元素所在层数 //树更易实现动态查找 //结点的度:子树个数 //树的度:所有结点度最大的 //树的表示:儿子兄弟表示法 ->旋转45° 变成二叉树 //element:left ,right //二叉树有左右之分,而一般的树没有 //树操作,有判断是否空,遍历和创建二叉树 //遍历有先序,中序,后序和层次遍历 //完全二叉树用顺序存储方便之处,是父子关系可通过序号关系查找; //一般二叉树也可以先补充为满二叉树,用数组存储,但会造成空间浪费 //------------------------------------------------------------------------------------------------树的链表表示 //链表表示树

typedef struct TreeNode *BinTree; typedef BinTree Position; struct TreeNode{ ElementType Data; BinTree Left; BinTree Right; } //先序遍历 Void PreOrderTraversal( BinTree BT) { if(BT){ printf("%d", BT->Data); PreOrderTraversal(BT->Left); PreOrderTraversal(BT->Right); } } //先序,中序,后序都是以根节点为入口,再以根结点为出口,只不过是什么时候访问(print)结点不同 //中序遍历非递归遍历算法 void InOrderTraversal(BinTree BT) { BinTree T = BT; Stack S = CreateStack(MaxSize); //创建并初始化堆栈S while(T || !IsEmpty(S)){ while(T){ //一直向左并将沿途压入堆栈 Push(S, T); T = T->Left; } if(!IsEmpty(S)){ T = Pop(S); printf("%5d", T->Data); //结点弹出堆栈 T = T->Right; //转向右子树 } } } //层序遍历,通过队列实现,把结点进队,退出后,把左右儿子入队。 void levelOrderTraversal(BinTree BT) { Queue Q; BinTree T; if (!BT) return; //空树则直接返回 Q = CreateQueue(MaxSize); //创建并初始化队列Q AddQ(Q, BT); while (!IsEmptyQ(Q)){ T = DeleteQ(Q); printf("%d\n", T->Data); //访问取出队列的结点 if (T->Left) AddQ(Q, T->Left); if (T->right) AddQ(Q, T->Right); } } //可以求叶子节点,二叉树高度 //二元运算表达式树,中缀表达式会受到运算优先级的影响,但可以加括号的方式避免 //-------------------------------------------------------------------------------------------二叉搜索树,左子树小,右子树大 //二叉搜索树,也称二叉排序树 //1.非空左子树的所有键值小于其根节点的键值 //2.非空右子树的所有键值大于其根节点的键值。 //3.左右子树都是二叉搜索树
//查找效率决定于树的高度
Position Find(ElementType X, BinTree BST)
{
  if(!BST) return NULL; //空则返回NULL
  if(X > BST->Data) return Find(X, BST->Right); //在右树继续查找
  else if(X < BST->Data) return Find(X, BST->Left); //在左树继续查找
  else return BST; //查找成功,返回找到的结点地址
}

//由于非递归函数的执行效率高,可将尾递归(return时递归)函数改为迭代函数,->循环 //最大元素是最右走到底,最小元素是最左走到底

Position IterFind(ElementType X, BinTree BST)
{
  while(BST){
    if(X > BST->Data) BST = BST->Right; //在右树继续查找
    else if(X < BST->Data) BST = BST->Left; //在左树继续查找
    else return BST; //查找成功,返回找到的结点地址
   }
   return NULL; //查找失败
} //insert操作,如何插入到二叉搜索树 BinTree Insert(ElementType X, BinTree BST) { if(!BST){ BST = malloc(sizeof(struct TreeNode)); BST->Data = X; BST->Left = BST->Right = NULL; }else if(X < BST->Data) BST->Left = Insert(X, BST->Left); else if (X > BST->Data) BST->Right = Insert(X, BST->Right); return BST; } //delete 操作 //删除叶子很简单,但删除结点,找左子树最大值或右子树最小值做结点 BinTree Delete(ElementTyoe X, BinTree BST) { Position Tmp; if (!BST) printf("要删除元素未找到"); else if(X < BST->Data) BST->Left = Delete(X, BST->Left); //左子树递归删除 else if(X > BST->Data) BST->Right = Delete(X, BST->Right); //右子树递归删除 else //找到要删除节点 if (BST->Left && BST->Right){ //删除节点有两个子结点 Tmp = FindMin(BST->Right); //在右子树找最小元素填充被删除结点 BST->Data = Tmp->Data; BST->Right = Delete(BST->Data, BST->Right); //在删除结点的右子树中删除最小元素 } else{ //被删除结点有一个或无子结点 Tmp = BST; if(!BST->Left) BST = BST->Right; //有右孩子或无子结点 else if(!BST->Right) BST = BST->Left; //有左孩子或无子结点 free(Tmp); } return BST; } //ASL 平均查找长度 //AVL树,平衡二叉树;平衡因子BF=hL-hR(对结点来说),任一结点左右子树高度差的绝对值不超过1. 复杂度log2(n) //平衡二叉树调整,RR, LL, RL, LR旋转等

//summary:,完全二叉树(结点编号与满二叉树相同的),满二叉树(完美二叉树),二叉搜索树,平衡二叉树

 

//堆 heap
//优先队列priority queue, 取出元素的顺序依照元素的优先权大小。
//----------------------------------------------------------------------------------------------------------------第四部分:堆,有优先级的结构 //通过数组实现
typedef struct HeapStruct *MaxHeap; struct HeapStruct{ ElementType *Element; //存储堆元素的数组 int Size; //堆的当前元素个数 int Capacity; //堆的最大容量 }; //创建一个堆 MaxHeap Create(int MaxSize) { //创建容量为Maxsize的空最大堆 MaxHeap H = malloc(sizeof(struct HeapStruct)); H->Elements = malloc((MaxSize+1)*sizeof(ElementType)); H->Size = 0; H->Capacity = MaxSize; H->Elements[0] = MaxData; //定义哨兵为大于堆中所有可能元素的值 return H; }
//有序队列的完全二叉树表示堆,堆的两个特性,结构性:用数组表示的完全二叉树,有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)

//数据插入堆 void Insert(MaxHeap H, ElementType item) { //将元素item插入最大堆H,其中H->Elements[0]定义为哨兵 int i; if (IsFull(H)){ printf("最大值已满"); return; } i = ++H->Size; //i指向插入后堆中最后一个元素的位置 for(; H->ElementType[i/2] < item; i/2) H->Elements[i] = H->Elements[i/2]; //向下过滤结点 H->Elements[i] = item; //将item插入 } //delete 操作 ElementType DeleteMax(MaxHeap H) { //从最大堆H中取出最大值,并删除 int Parent, Child; ElementType MaxItem, temp; if(IsEmpty(H)){ printf("最大堆已空"); return; } MaxItem = H->Elements[1];//取出最大值 temp = H->Elements[H->Size--];//用最大堆中最后一个元素从根节点向上过滤下层结点 for(Parent = 1; Parent*2 <= H->Size; Parent = Child){ Child = Parent*2; if((Child != H->Size) && (H->Elements[Child] < H->Elements[Child+1])) Child++; //child指向左右结点较大的 if(temp >= H->Elements[Child]) break; else H->Elements[Parent] = H->Elements[Child]; //移动temp元素到下一层 } H->Elements[Parent] = temp; return MaxItem; } //huffman tree 霍夫曼树,霍夫曼编码等等,排序,每两个最小权重合并,构成左右子树,然后依次操作。 typedef struct TreeNode *HuffmanTree; struct TreeNode{ int Weight; HuffmanTree Left, Right; }; HuffmanTree Huffman(MinHeap H) { //假设H->Size个权值已经存在H->Element[]->Weight中 int i; HuffmanTree T; BuildMinHeap(H); //将H->Element[]按权值调整为最小堆 for (i = 1; i < H->Size; i++){ //做H->Size -1次合并 T = malloc(sizeof(struct TreeNode)); //建一个新结点 T->Left = DeleteMin(H); //从最小堆删除一个结点,作为新T的左子结点 T->Right = DeleteMin(H); //从最小堆删除一个结点,作为新T的右子结点 T->Weight = T->Left->Weight + t->Right->Weight; //计算新权重 Insert(H, T); //将新T插入 } T = DeleteMin(H); return T; } //---------集合简单表示--------------------------------------------------- typedef struct { ElementType Data; int Parent; }SetType;
//------------------------------------------------------------------------ //按秩归并与路径压缩 可提高算法的效率,但是路径压缩在数据量很大的情况下才能显示出优势来
//总结一下,数组与链表是存储最最基本的两种结构,由此衍生出stack, heap, queue, tree等等抽象数据类型,堆栈后进先出,队列先进先出,堆有优先级的队列(可用数组,链表,完全二叉树表示),树的结构形式在查询中有优势。

 

上一篇:938. Range Sum of BST


下一篇:leetcode230 - Kth Smallest Element in a BST