数据结构简介



  • 在团队有一些其他系的同学可能并没有学过数据结构,所以在此就拿出之前上数据结构课程时候的总结与大家分享。由于数据结构这门课程与使用语言无关,所以以下内容使用的是类c语言的伪码编写,应该比较容易看懂。

    伪码基本操作

    比较

    • EQ // EQUAL等于
    • NE // NOT EQUAL不等于
    • GT // GREATER THAN大于
    • LT // LESS THAN小于
    • GE // GREATER THAN OR EQUAL 大于等于
    • LE // LESS THAN OR EQUAL 小于等于

    线性表

    • InitList( &L ); //建空表,初始化
    • DestoryList( &L ); //撤销表,释放内存
    • int LengthList( L ); //求表中元素个数,即表长
    • POSITION LocateElem (L,ElemType e,compare() ) //查找e
    • PriorElem( L, cur_e, &pre_e ); //求当前元素e的前驱
    • NextElem( L, cur_e, &next_e ); //求当前元素e的后继
    • ListInsertBefore(&L, i, e ); //把e插入到第i个元素之前
    • ListDelete( &L, i,&e ); //删除第i个元素并“看”此元素
    • ListTraverse( L, Visit() ); // “看”表中全部元素(遍历)

    • InitStack(&S); //构造一个空栈
    • DestroyStack(&S); //销毁栈s
    • ClearStack(&S); //将S清为空栈
    • StackEmpty(S); //若栈S为空栈,返回True; 否则返回 False
    • StackLength(S); //返回栈S的元素个数,即栈的长度
    • GetTop(S, &e); //用e返回栈S的栈顶元素
    • Push(&S, e); //插入元素e为新的栈顶元素
    • Pop(&S, &e); //删除S的栈顶元素,并用e返回其值

    队列

    • InitQueue(&Q); //构造一个空队列Q
    • DestroyQueue(&Q); //销毁队列Q
    • QueueLength(Q); //返回队列Q的长度
    • ClearQueue(&Q); //将队列清为空队列
    • GetHead(Q, &e); //用e返回Q的队头元素
    • EnQueue(&Q, e); //插入元素e为Q的新的队尾元素
    • DeQueue(&Q, &e); //删除Q的队头元素,并用e返回其值

    • StrAssign(&T, chars); // 串赋值,生成值为chars的串T
    • StrCompare(S,T); // 串比较,若S>T,返回值大于0…
    • StrLength(S); // 求串长,即返回串S中的元素个数
    • Concat(&T, S1, S2); // 串连接,用T返回S1+S2的新串
    • SubString(&Sub, S, pos, len); //求S中pos起长度为len的子串

    广义表

    • GetHead(L); //取广义表L的头
    • GetTail(L); //取广义表L的尾

    • CreateGraph(&G, num); //构造一个顶点数为num,边数为0的图
    • DestroyGraph(&G); //销毁图 G
    • FirstAdjVex(G, v); //返回 v 的第一个邻接点
    • NextAdjVex(G, v, w); //返回 v 的(相对于 w 的)下一个邻接点
    • InsertEdge(&G, v, w); //在 G 中增添以v, w为顶点的边
    • DeleteEdge(&G, v, w); //在 G 中删除以v, w为顶点的边

    基础知识点

    线性表

    • 顺序表的插入与删除(时间复杂度O(n))
    • 链表节点的插入,删除,查找
      • 插入方法有头插法和尾插法,删除类比插入
      • 单链表查找的时间复杂度为O(n)
      • 插入与删除时间复杂度为O(1)
    • 双向链表,循环链表,双向循环链表,静态链表的基本操作

    栈与队列

    栈与队列是特殊的链表

    • 栈的应用——表达式求值
    • 循环队列
      • 在循环队列中,空队和队满时都有front=rear;判决条件将出现二义性
      • 解决方法:
        • 使用一个计数单元记录队列中的元素个数
        • 加设标志位,删除时置1,插入时置0(分离插入后和删除后出现判决条件)
        • 空一个单元,队满特征改为front=(rear+1)%N

    二叉树

    • 二叉树的遍历:

      • 先序遍历
      • 中序遍历
      • 后序遍历
      • 层次遍历
      • 遍历算法的应用:
        • 输出二叉树的节点
        • 输出二叉树的叶子节点
        • 统计叶子节点数目
        • 求二叉树的高度
        • 遍历主要分为整体遍历和分别遍历左右子树两种
    • 线索二叉树:空指针指向父节点

    • 树和二叉树之间的转换

      • 树转二叉树(兄弟相连 长兄为父 头树为根 孩子靠左)
      • 二叉树转树(右孩子变为兄弟)
    • Huffman树(最优二叉树)

      for(i=n+1;i<=m;++i)
      {                         //建哈夫曼树,并保存树的父子关系
      Select(Ht,i-1,s1,s2);                      
      Ht[s1].parent=i;Ht[s2].parent=i;
      Ht[i].lchild=s1;Ht[i].rchild=s2;
      Ht[i].weight=Ht[s1].weight+Ht[s2].weight;
      }
      
      // 从叶子到根逆向求每个字符的哈夫曼编码 
      cd[n-1]='\0';                 //编码结束符 
      int start;
      for(i=1;i<=n;++i){          //逐个字符求哈夫曼编码
      start=n-1;
      for(c=i,f=Ht[i].parent;f!=0;c=f,f=Ht[f].parent)
      {if(Ht[f].lchild==c)cd[--start]='0';
      else cd[--start]='1';
      }
      //cd[n-1]='\0';
      Hc[i]=(char *)malloc((n-start)*sizeof(char));
      strcpy(Hc[i],&cd[start]);
      }
      

    • 图的遍历

      • 深度优先搜索(DFS)
      void DFSTraverse(Graph G,int v)
      {   /* 对图 G 作深度优先遍历 */
      for (v=0; v<G.vexnum; ++v) 
          visited[v] = FALSE; // 访问标志数组初始化
      for (v=0; v<G.vexnum; ++v) 
          if (!visited[v]) // 对尚未访问的顶点调用DFS
              DFS(G, v);             
      }
      
      void DFS(Graph G, int v) {
          /* 从顶点v出发,深度优先搜索遍历连通图 G */
          visited[v] = TRUE; //访问第v个顶点
          VisitFunc(v); 
          for( w=FirstAdjVex(G,v) ; w!=0 ; w=NextAdjVex(G,v,w ) )
                  if (!visited[w])  
                      DFS(G, w);     
      } // DFS   
      
      • 广度优先搜索(BFS)
      void BFSTraverse(Graph G,int v) {
          for (v=0; v<G.vexnum; ++v) 
              visited[v] = FALSE;      //初始化
          InitQueue(Q); 			//置空的辅助队列Q
          for (v=0; v<G.vexnum; ++v)      //最外层大循环,每个顶点都要搜索
              if (!visited[v]) {      //v尚未访问
                  visited[v]=TRUE; visit(v);
                  EnQueue(Q,  v);         //v入队列
                  while (!QueueEmpty(Q)) {
                      DeQueue(Q,  u);         //对头元素出队,记为u
                      for (w=FirstAdjVex(G,u); w; w=NextAdjVex(G,u,w))
                          if (! visisted[w]) {//w为u的尚未访问的邻接顶点
                              visisted[w] = TRUE;
                              EnQueue(Q,w);
                          }
                  }
              }
      }//BFSTraverse
      
    • 最小生成树问题

      • Prim算法
      void Prim(MGraph G, VertexType u)
      {//用PRIM算法从第u个顶点出发构造网G的最小生成树T。
          k=LocateVex(G,u);
          closedge[k].lowcost = 0;
          for (j=0; j<G.vexnum; ++j) //辅助数组初始化
              if (j!=k)  // adjvex, lowcost
                  closedge[j]={u, G.arcs[k][j].adj;}
          for (i=1; i<G.vexnum; ++i)
          {
              k=minimum(closedge); //求出T的下一个节点
              printf(closedge[k].adjvex, G.vexs[k]); //输出生成树的边
              closedge[k].lowcost = 0; // 第k顶点并入U集
              for (j=0; j<G.vexnum; ++j) //更新节点间的距离
                  if (G.arc[k][j].adj<closedge[j].lowcost)       
                      closedge[j]={G.vexs[k], G.arcs[k][j].adj}; 
          }
      }
      
      • Kruskal算法(并查集思路)
      struct Edge{  
          int a;  
          int b;  
          int weight;  
      }; //a->b=weight
      
      void Kruskal(MGraph G) //图以边集数组的形式存储
      {
          int parent[MAXVEX];
          for(i=0;i<G->numVertexes;++i)  //初始化,
              parent[i]=0;
              //元素值parent[i]非0时表示结点i与parent[i]之间的边已确定
          i=min(G->edges).index //找到最短边
          while(1)
          {
              n=Find(parent,G->edges[i].a);
              m=Find(parent,G->edges[i].b);
              if(n!=m) //m,n原本没有连接(避免成环)
              {
                  parent[n]=m; //连接m,n
                  printf("xxx");
              }
              i=min(G->edges).index //找到最短边
          if(IsCompleted(parent))  //判断最小生成树是否完成
              return;  
          }
      }
      
      int Find(int *parent,int f)  //找到f的最前父节点
      {  
          while(parent[f]!=0) 
              f=parent[f];  
          return f;  
      }
      
    • 最短路径问题

      • Dijkstra算法
      void Dijkstra(MGraph G, int v0, PathMatrix *P, ShortPathTable *D)
      {
          for (v=0; v<G.vexnum; ++v)  //初始化
          {
              final[v] = FALSE;  //判断是否得到最短路径
              D[v] = G.arcs[v0][v];
              if (D[v]<INFINITY) { P[v0][v] = v;}
              for (w=0; w<G.vexnum; ++w)
                  if(G.arcs[v][w]<INFINITY)
                      P[v][w] = w;
          }
          D[v0]=0; final[v0]=TRUE
          
          for (i=1; i<G.vexnum; ++i) 
          {
              min = INFINITY;
              for (w=0; w<G.vexnum; ++w)  //找到最近顶点
                  if (!final[w])
                      if  (D[w]<min) { v=w; min = D[w] } //w顶点离v0顶点更近
              final[v] = TURE; //离v0顶点最近的v加入S集
              for (w=0; w<G.vexnum; ++w) //更新当前最短路径及距离
              {
                  if (!final[w] && (D[v]+G.arcs[v][w]<D[w]))
                  {
                      D[w] = D[v] + G.arcs[v][w];
                      P[v0][w] = v;
                  }
              }
          }
      }
      
      • Floyd算法(kij算法)
      void Floyd(MGraph G,PathMatrix *P, ShortPathTable *D)
      {
          for(v=0;v<G->numVertexes;++v)  //初始化
          {  
              for(w=0;w<G->numVertexes;++w)  
              {  
                  D[v][w]=G->arc[v][w];  
                  P[v][w]=w;
              }
          }
      
          for(k=0;k<G->numVertexes;++k)  
              for(v=0;v<G->numVertexes;++v)  
                  for(w=0;w<G->numVertexes;++w)    
                      if( D[v][w]>D[v][k]+D[k][w] )
                      {  
                          D[v][w] = D[v][k] + D[k][w];  
                          P[v][w] = P[v][k];
                      }
      }
      

    查找

    • 二分查找(O(log(n)))
    int Search_Bin ( SSTable ST , KeyType key ) 
    {   low = 1;  high = ST.length;     // 置区间初值
       while (low <= high)             //二分查找
        { mid = (low + high) / 2;
          if (EQ (key , ST.elem[mid].key) )
              return  mid;                     // 找到待查元素
          else  if ( LT (key , ST.elem[mid].key) )
              high = mid - 1;       // 继续在前半区间进行查找
           else  low = mid + 1; // 继续在后半区间进行查找
          }
       return 0;    
    
    • 二叉查找树
    BiTree SearchBST (BiTree T, KeyType key, ) 
    {  // 在根指针 T 所指二叉排序树中递归地查找其关键字等于 key的数据元素,若查找成功,则//返回指向该数据元素 的指针否则表明查找不成功,返回空指针NULL
        if( (!T)||EQ(key,T->data.key) )    return(T);
        else 
            if ( LT(key,T->data.key) )
                return(SearchBST(T->lchild,key));
            else  return (SearchBST(T->rchild,key));
    } // SearchBST
    
    
    • 二叉平衡树(log(n))

    • Hash表冲突处理方法

      • 开放定址法
        • 线性探测法 Hi=(Hash(key)+di) mod m(di为i)
        • 二次探测法 Hi=(Hash(key)±di) mod m(di为i的平方)
        • 若di=伪随机序列,就称为伪随机探测法
      • 链地址法
      • 再哈希法
        • Hi=RHi(key)
      • 建立一个公共溢出区

    排序

    • 稳定排序与不稳定排序

    • 内部排序与外部排序

    • 插入排序

      • 直接插入排序(时间复杂度O(n2),空间复杂度O(1),稳定排序)
      • 折半插入排序,2-路插入排序,表插入排序
      • 希尔排序(时间复杂度(O(n1.25)~O(1.6n1.25)),空间复杂度O(1),不稳定排序)
    • 交换排序

      • 冒泡排序((时间复杂度O(n2),空间复杂度O(1),稳定排序))
        • 优化方式:
          • 双向冒泡排序
          • 设置一个标记变量flag,当循环中没有交换数据时,停止循环。
      • 快速排序
      void quick_sort(int s[],int l,int r)
      {
          if(l < r)
          {
              int i=l,j=r,m=s[l];
              while(i<j)
              {
                  while(i<j && s[j]>=m)   //从右到左找到第一个小于m的数  
                      j--;              
                  while(i<j && s[i]<=m)   //从左往右找到第一个大于m的数  
                      i++;
                  if(i<j)
                  {
                      int temp1 = s[j];
                      s[j] = s[i];
                      s[i] = temp1;
                  }
                  
              }
              int temp2 = s[j];
              s[j] = s[i];
              s[i] = temp2;
      
              quick_sort(s,i,m-1);    //递归调用 
              quick_sort(s,i+1,r);        
          }
      }
      
    • 选择排序

      • 简单选择排序
      • 锦标赛排序(时间复杂度O(nlog2n),空间复杂度O(n),稳定排序)
      • 堆排序(时间复杂度O(nlog2n),空间复杂度O(1),不稳定排序)
      void HeapSort (HeapType &H ) {
          for ( i = H.length / 2;  i >0; - - i )
              HeapAdjust(H,i, H.length );     //for,建立初始堆
          for ( i = H.length; i > 1; - -i) { 
          H.r[1] ←→ H.r[i];      //交换,要借用temp
          HeapAdjust( H, 1,i-1 );   //重建最大堆,  i-1=m
          } 
      }
      
      
      HeapAdjust(HeapType *H,current,m){
          child = 2 * current;
          while(child<=m){
              //选取child中的较大值
              child = (H[child].key>H[child+1].key) ? child : child+1 ; 
              if(H[current].key<H[child].key){ //将较大值上移
                      H[current] ←→ H[child];
                      current = child;
                      child *= 2;
              }
              else break;
          }
      }
      
      
    • 归并排序(时间复杂度O(nlog2n),空间复杂度O(n),稳定排序)

        void MSort (SR[ ],&TR1[ ],s, t) {  
            // 将无序的SR[s…t]归并排序为TR1[s…t]
            if ( s==t ) TR1[s]=SR[s];
            else{
                m=(s+t)/2;
                MSort (SR,&TR2,s, m);
                MSort (SR,&TR2,m+1, t );
                Merge(TR2, TR1, s, m, t );
            }   
        }
    
        void Merge (SR,&TR,i, m, n) {
            // 将有序的SR[i…m]和SR[m+1…n]归并为有序的TR[i…n]
            for(k=i , j=m+1;  i<=m && j<=n;  ++k ) {
                if ( SR[i]<= SR[j] )  TR[k]=SR[i++];
                else TR[k]=SR[j++]; // 将两个SR记录由小到大并入TR
            } // for
            if (i<=m) TR[k…n]=SR[i…m]; // 将剩余的SR[i…m]复制到TR
            if (j<=n) TR[k…n]=SR[j…n]; // 将剩余的SR[j…n]复制到TR
        }  // Merge
    
    
    • 基数排序(时间复杂度O(d(n+radix)),空间复杂度(O(n+radix)),稳定排序)

      • MSD 最高位优先法
      • LSD 最低位优先法
    • 八大排序的比较
      八大排序的比较

    参考资料

    八大排序源码与分析


 

Copyright © 2018 bbs.dian.org.cn All rights reserved.

与 Dian 的连接断开,我们正在尝试重连,请耐心等待