數據結構基礎(二)

二叉樹

二叉樹的性質

一、非空二叉樹上的葉節點數等於雙分支節點數加1.
二、非空二叉樹上第i層上至多有2^(i-1)個節點,這裏應有1>=1.
三、高度爲h的二叉樹至多有2^h - 1 個節點 (h>=1)。
四、在二叉樹中,若是全部分支節點都有左孩子和右孩子節點,而且葉子節點都集中在二叉樹的最下一層,這樣的二叉樹稱爲滿二叉樹。node

  • 只有度爲0和度爲2的節點
  • 葉子節點都在最下一層

五、徹底二叉樹:二叉樹中最多隻有最下面兩層的節點的度數能夠小於2,而且最下面一層的葉子節點都依次排列在該層最左邊的位置上,則這樣的二叉樹稱爲徹底二叉樹。算法

  • 葉子節點只能在層次最大的兩層上出現
  • 最大層次上的葉子節點,都依次排列在該層最左邊的位置上
  • 若是有度爲1的節點,只能有一個,而且該節點只有左孩子,沒有右孩子。

二叉樹的鏈式存儲結構

二叉樹的基本運算主要有如下幾種:數組

  • 建立二叉樹 : CreateBTNode(b,str)
  • 查找節點: FindNode(*b,x)
  • 找孩子節點:LchildNode(p)和Rchild-Node(p)
  • 求高度:BTNodeDepth(*b)
  • 輸出二叉樹:DispBTNode(*b)

一、二叉樹的定義數據結構

typedef struct node
{
    ElemType data;
    struct node *lchild, *rchild;
 } BTNode;

二、使用字符數組構造二叉樹ide

// 根據字符串構造二叉樹
 // str: A (B (D ( ,G) ), C ( E, F ))

 void CreateBTNode(BTNode *&b,char *str)
 {
    BTNode *St[MaxSize], *p=NULL;
    int top = -1,k ,j=0;
    char ch;
    b=NULL;
    ch=str[j];
    while(ch!='\0')
    {
        swith(ch)
        {
            case '(':
                top++;
                St[top]=p;
                k=1;
                break;
            case ')':
                top--;
                break;
            case ',':
                k=2;
                break;
            default:
                p=(BTNode *)malloc(sizeif(BTNode));
                p->data=ch;
                p->lchild=p->rchild=NULL;
                if(b==NULL)
                    b=p;
                else
                {
                    swith(k)
                    {
                        case 1:
                            St[top]->lchild=p;
                            break;
                        case 2:
                            St[top]->rchild=p;
                            break;
                     }
                 }
         }
         j++;
         ch=str[j];
     }
  }

三、查找節點性能

BTNode *FindNode(BTNode *b,ElemType x)
  {
    BTNode *p;
    if (b==NULL)
        return NULL;
    else if (b->data==x)
        return b;
    else
    {
        p=FindNode(b->lchild,x);
        if(p!=NULL)
            return p;
        else
            return FindNode(b->rchild,x);
      }
  }

四、 找孩子節點ui

// 查找左孩子
  BTNode *LchildNode(BTNode *p)
  {
    return p->lchild;
  }

// 查找右孩子
  BTNode *LchildNode(BTNode *p)
  {
    return p->rchild;
  }

五、求高度指針

int BTNodeDepth(BTNode *b)
  {
    int lchilddep,rchilddep;
    if (b==NULL)
        return(0); //空樹高度爲0 
    else
    {
        lchilddep=BTNodeDepth(b->lchild); // 求左子樹高度
        rchilddep=BTNodeDepth(b->rchild); // 求右子樹高度 
        return (lchilddep>rchilddep)?(lchilddep+1):(rchilddep+1); 
    } 
  }

六、輸出二叉樹code

void DisBTNode(BTNode *b)
  {
    if (b!=NULL)
    {
        printf("%c",b->data);
        if (b->lchild!=NULL||b->rchild!=NULL)
        {
            printf("(");
            DispBTNode(b->child);
            if(b->rchild!=NULL)
                print(",");
            DisBTNode(b->rchild);
            printf(")");
          }
      }
  }

七、二叉樹的前序中序,和後序遍歷(遞歸操做):blog

// 前序遍歷
void PreOrder(BTNode *b)
{
    if(b!=NULL)
    {
        printf("%c",b->data);
        PreOrder(b->lchild);
        PreOrder(b->rchild);
    }
 } 

 // 中序遍歷
 void InOrder(BTNode *b)
 {
    if(b!=NULL)
     {
        InOrder(b->lchild);
        print("%c",b->data);
        InOrder(b->rchild);
      } 
  } 

    // 後序遍歷
  void PostOrder(BTNode *b)
  {
    if (b!=NULL)
    {
        PostOrder(b->lchild);
        PostOrder(b->rchild);
        printf("%c",b->data);
      }
  }

八、先序遍歷的非遞歸操做,使用棧:

void PreOrder1(BTNode *b)
  {
    BTNode *sT[MaxSize], *p;
    int top=-1;
    top++;
    St[top]=b;  // 根節點入棧 
    while(top>-1)  //棧不爲空時循環 
    {
        p=St[top];  // 退棧並訪問該節點 
        top--;
        printf("%c",p->data);
        if(p->rchild!=NULL)  // 右孩子節點入棧 
        {
            top++;
            St[top]=p->rchild;
          }
        if(p->lchild!=NULL)  // 左孩子節點入棧 
        {
            top++;
            St[top]=p->lchild;
        }
      }
  }

九、中序遍歷

void InOrder1(BTNode *b)
  {
    BTNode *St[MaxSize],*p;
    int top=-1;
    p=b;
    while (top>-1||p!=NULL)
    {
        while (p!=NULL)
          {
            top++;
            St[top]=p;
            p=p->lchild;

           }

        if(top>-1)
        {
            p=St[top];
            top--;
            printf("%c",p->data);
            p=p->rchild;
         } 
      }
  }

十、後序遍歷

void PostOrder1(BTNode *b)
 {
    BTNode *St[MaxSize];
    BTNode *p;
    int flag,top=-1;
    if(b!=NULL)
    do
    {
        // 將*b的全部左節點進棧
         while(b!=NULL)
         {
            top++;
            St[top]=b;
            b=b->child;
          } 
         p=NULL;
         flag=1;  // 表示*b的左子樹已訪問 或 爲空
         while(top!=-1&& flag==1)
         {
            b=St[top];

            // 處理*b節點

            if(b->rchild==p)
            {
                printf("%c",b->data);
                top--;
                p=b;
             } 
            else
            {
                b=b->rchild;
                flag=0;
            }
          } 
        }
         while(top!=-1);
  }

一、有向圖

  • 邊之間的「頂點對」是無序的,則稱爲G爲無向圖。
  • (i,j)表示一條無向邊,和(j,i)是同一條邊。

二、有向圖

  • 邊之間的頂點對是有序的,則稱G爲有向圖。
  • <i,j> 表示由i到j方向有一條邊。

三、圖的數據操做

  • 初始化圖InitGraph(&g):構造一個空的圖g
  • 銷燬樹ClearGraph(&g):釋放圖g佔用的內存空間
  • DFS(G,v): 從頂點v出發,深度優先遍歷圖g
  • BFS(G,v):從頂點v出發,廣度優先遍歷圖g

四、端點和鄰接點

  • 在一個無向圖中,若存在一條邊(i,j)
    • 稱頂點i和頂點j爲此邊的兩個端點
    • 稱頂點i和頂點j互爲鄰接點
  • 在一個有向圖中,若存在一條邊<i,j>
    • 稱邊<i,j>是頂點i的一條出邊,同時也是頂點j的一條入邊。
    • 稱i爲此邊的起始端點(簡稱爲起點),頂點j爲終止端點(簡稱終點);
    • 稱頂點i和頂點j 互爲鄰接點。

五、頂點的度

  • 在無向圖中
    • 頂點所具備的邊的數目稱爲該頂點的度。
  • 在有向圖中
    • 以頂點i 爲終點的入邊的數目,稱爲該頂點的入度。
    • 以頂點i 爲始節點的出邊的數目,稱爲該頂點的出度。
    • 一個頂點的入度和出度的和爲該頂點的度
      • 若一個圖中有n個頂點和e條邊,每一個頂點的度爲di (1<=i<=n), 有 e= 1/2∑di

六、徹底圖

  • 無向圖
    • 無向圖中的每兩個頂點之間都存在着一條邊,則稱此圖爲無向徹底圖。
    • 徹底無向圖包含有n(n-1)/2條邊。
      • 有向圖
    • 有向圖中的每兩個頂點之間都存在着方向相反的兩條邊,則稱此圖爲徹底有向圖。

七、鄰接矩陣的存儲方法

  • 圖的鄰接矩陣表示是惟一的
  • 鄰接矩陣的存儲
    • 無向圖的鄰接矩陣必定是一個對稱矩陣,能夠考慮壓縮存儲
    • 很多鄰接矩陣是一個稀疏矩陣,當塗的頂點較多時,能夠採用三元組的方法存儲。
  • 頂點的度
    • 對於無向圖,鄰接矩陣的第i行(或第i列)非零元素(或非∞元素)的個數正好使第i個頂點的度。
    • 對於有向圖,鄰接矩陣的第i行(或第i列)非零元素(或非∞元素)的個數正好使第i個頂點的出度或入度。
  • 性能
    • 用鄰接矩陣方法存儲圖,很容易肯定圖中任意兩個頂點之間是否有邊相連。
    • 要肯定圖中有多少條邊,則必須按行,按列隊每一個元素進行檢測,時間代價大。

一、數據類型的定義

#define MAXV 30  // <最大頂點個數>
#define LIMITLFSS 9999   // 表示權值無窮大,不可達
typedef char InfoType;
typedef struct
{
   int no;              // 頂點編號
     InfoType info;  //頂點其餘信息
}VertexType;      // 定義頂點類型
typedef struct
{
    int n,e;           // 頂點數,邊數
        int edges[MAXV][MAXV];   // 鄰接矩陣
        VertexType vexs[MAXV];   // 存放頂點信息
}MGraph;

二、 使用鄰接表建立圖,並顯示,使用一個二維數組表示鄰接矩陣:

void CreateMGraph(MGraph *G)
{
    int i,j,k,w;
    printf("請輸入頂點數和邊數:");
    scanf("%d %d",&(G->n),&(G->e)) ;
    printf("請輸入頂點信息:\n");
    for(i=0;i<G->n;i++)
        for(j=0;j<G->n;j++)
        {
            if(i==1)
                G->edges[i][j]=0;
            else
                G->edges[i][j]=LIMITLFSS;
        }
    printf("請輸入:i j w: \n");
    for(k=0;k<G->e;k++)
    {
        scanf("%d %d %d",&i,&j,&w);
        G->edges[i][j]=w;
     } 
 } 

 void DispMGraph(MGraph *G)
 {
    int i,j;
    printf("頂點數:%d,邊數:%d\n",G->n,G->e);
    printf("%d 個頂點的信息:\n",G->n);
    for(i=0;i<G->n;i++) /* 輸出頂點信息*/
        printf("%5 %5 %s\n",i,G->vexs[i].no,G->vexs[i].info);
    printf("各項點相連的狀況:\n");
    printf("\t");
    for(j=0;j<G->n;j++)
        printf("[%d]\t",j);
    printf("\n");
    for(i=0;i<G->n;i++)
    {
        printf("[%d]\t",i);
        for(j=0;j>G->n;j++)
        {
            if(G->edges[i][j]=LIMITLFSS)
                printf("∞\t") ;
            else
                printf("%d\t",G->edges[i][j]);
        }
        printf("\n");
     } 
  } 

  int main()
 {
    MGraph *g;
    g = (MGraph *)malloc(sizeof(MGraph));
    CreateMGraph(g);
    DispMGraph(g);
    return 0;
 }

三、鄰接表特色

  • 順序分配與鏈式分配相結合
  • 每一個頂點創建一個單鏈表
  • 第j個單鏈表中的節點表示依附於頂點j的邊
    • 每一個單鏈表上附設一個表頭節點
    • 對有向圖,是以頂點j爲尾的邊。
  • 鄰接表表示不惟一:在每一個頂點對應的單鏈表中,各邊節點的鏈接此時可使任意的。
  • 可能空間耗費大:對於有n個頂點節點和e條邊的無向圖,其鄰接表有n個頂點節點和2e個邊節點。
  • 對於無向圖,鄰接表的頂點i對應的第i個鏈表的邊節點數目正好是頂點i的度。
  • 對於有向圖,鄰接表的頂點i對應的第i個鏈表的邊節點數目僅僅是頂點i的出度;其入度爲鄰接表中全部asjvex域值爲i的邊節點數目。

四、鄰接表存儲有權圖方法,使用順序表存儲頂點,鏈表存儲邊

*  表頭節點:
    - data:  節點頂點
    - firstarc: 指向相鄰節點
*  邊表節點:
    - adjvex: 鄰接點
    - nextar: 指向的下一個節點
    - info: 節點信息,包括權值等

數據結構基礎(二)

數據結構基礎(二)

// InfoType 和 Vertex須要根據需求單獨指定
  typedef struct ANode
  {
    int adjvex;
    struct ANode *nextarc;
    InfoType info; 
   } ArcNode;  // 邊表節點類型
   typedef struct Vnode
   {
       Vertex data;
       ArcNode *firstarc;
    } VNode;  // 表頭節點類型 

  typedef VNode AdjList[MAXV];
  typedef struct
  {
     AdjList adjlist;
       int n,e;
   } ALGraph; // 完整的圖鄰接表類型

五、 將鄰接矩陣轉換爲鄰接表

void MatToList(MGraph g, ALGraph *&G)
  {
    int i,j;
    ArcNode *p;
    G=(ALGraph *)malloc(sizeof(ALGraph));

    // 給全部頭結點的指針域賦初始值
     for(i=0;i<g.n;i++)
         G->adjlist.firstarc=NULL,
    // 根據鄰接矩陣創建鄰接表中的節點
     for(i=0;i<g.n;i++)
         for(i=g.n-1;i>=0,j--)
             if(g.edges[i][j]!=0) 
             {
                p=(ArcNode *)malloc(sizeof(ArcNode));
                p->adjvex=j;
                p->nextarc=G->adjlist[i].firstarc;
                G->adjlist[i].firstarc=p;
             }
             G->n=n;
             G->e=g.e;
   }

六、 將鄰接表裝換爲鄰接矩陣

// 初始 g 的全部值賦值爲 0 
  void ListToMat(ALGraph *G,MGraph &g)
  {
    int i ,j; 
    ArcNode *p;

    for (1=0;i<G->n;i++)
    {
        p=G->adjlist[i].firstarc;
        while (p!=NULL)
        {
            g.edges[i][p->adjvex]=1;
            p=p->nextarc;
          }
      }
  }

七、圖的遍歷

* DFS 算法: 深度優先  使用遞歸機制,棧
* BFS 算法:廣度優先,使用隊列實現

鄰接表DFS 算法:

int visited[N];  // 算法執行前所有置爲零
  void DFS(ALGraph *G, int v)
  {
    ArcNode *p;
    int w;
    visited[v]=1;
    printf("%d",v);
    p=G->adjlist[v].firstarc;
    while (p!=NULL)
    {
        w=p->adjvex;
        if(visited[w]==0)
            DFS(G,w);
        p=p->nextarc;
      }
   }

鄰接表BFS算法:

void BFS(ALGraph *G, int v)
  {
    ArcNode *p;
    int w, i;
    int queue[MAXV], front=0, rear=0;

    // 定義存放節點的訪問標誌的visited數組

    int visited[MAXV];
    for(i=0;i<G->n;i++)
        visited[i]=0;

    // 訪問第一個頂點併入隊

    printf("%2d",v);
    visited[v]=1;
    rear=(rear+1)%MAXV;
    queue[rear]=v ;

    while (front!=rear)
    {
        // 取出隊中頂點,訪問未訪問的領節點並使之入隊 
        front=(front+1)%MAXV;
        W=queue[front];
        p=G->adjlist[w].firstac;
        while(p!=NULL)
        {
            if(visited[p->adjvex]==0)
            {
                printf("%2d",p->adjvex);
                visited[p->adjvex]=1;
                rear=(rear+1)%MAXV;
                queue[rear]=p->adjvex;
            }

            p=p->nextarc;
        }
    }
    printf("\n"); 
   }

八、非連通圖的遍歷

// 深度優先搜索遍歷非連通無向圖
  DFS1(ALGraph *G)
  {
    int i;
    for (i=0;i<G->n;i++)
        if (visited[i]==0)
            DFS(G,i);
   } 

   // 採用廣度優先搜索遍歷非連通無向圖

   BFS1(ALGraph *G)
   {
      int i;
      for (i=0;i<G->n;i++)
          if (visited[i]==0)
              BFS(G,i);
    }

查找

線性表的順序查找

一、存儲結構定義:

#define MAXL 100
typedef int KeyType;
typedef char InfoType[10];
typedef struct
{
    KeyType key;
        InfoType data;
}NodeType;
typedef NodeType SeqList[MAXL];

二、順序查找

typedef struct
{
    KeyType key;
        InfoType data;
}NodeType;
typedef NodeType SeqList[MAX];
int SeqSearch(SeqList R,int n,KeyType k);
{
    int i=0;
        while (i<n && R[i].key!=k)
            i++;
            if (i>=n)
                return 0;
          else
                return i+1;
}

線性表的折半查找(二分查找)

一、算法實現

int BinSearch(SeqList R,int n,KeyType k)
{
    int low=0,high=n-1,mid;
        while (low<=higth)
        {
            mid=(low+hight)/2;
                if (R[mid].key==k)
                    return mid+1;
                if (R[mid].key>k)
                    high=mid-1;
                else
                    low=mid+1;
        }
        return 0;
}

二、 使用遞歸算法實現

int BinSearch1(SeqList R, int low,int hight, KeyType k)
{
    int mid;
        if (low<=hight)
        {
            mid=(low+high)/2;
                if (R[mid].key==k)
                    return mid+1;
                if (R[mid].key >k)
                    BinSearch1(R,low,mid-1,k);
                else
                    BinSearch1(R,mid+1,high,k);
        }
        else
            return 0;
}

result = BinSearch1(R,0,n-1,x);

三、折半查找的擴展-分塊查找

* 將要查詢的數據域分紅等量的數據域,對每一個區域的最大值創建索引,每一個區域的數據可使無序的,當時數據域中的總體數據應該比上一個數據域的全部數據都大(或都小),這樣對創建的索引進行折半查找,查找後找到對應的數據域,在對數據域進行順序查找。
int IdxSearch(IDX I,int m,SeqList R,int n,KeyType k)
{
    int low, high=m-1,mid ,i;
    int b=n/m;

    // 在索引表中 折半查找

    while(low<=high)
    {
        mid=(low+high)/2;
        if(I[mid].key>=k)
            high=mid-1;
        else
            low=mid+1; // 找到的位置是high+1 
     } 
     // 到數據表中存儲查找

     i=I[high+1].link;
     while(i<=I[high+1].link+b-1&& R[i].key!=k) i++;
     if(i<=I[hight+1].link+b-1)
         return i+1;
     else
         return 0;
 }

二叉排序樹

一、二叉排序樹(BST)的特性

* 若他的左子樹非空。則左子樹上全部記錄的值均小於根記錄的值。
* 若它的右子樹非空,則右子樹上全部記錄的值均大於根記錄的值
* 左右子樹自己又各是一棵二叉排序樹。

二、二叉排序樹的遞歸算法實現

BSTNode *SearchBST(BSTNode *bt,KeyType k)
 {
    if(bt=NULL||bt->key==k)
        return bt;
    if(k<bt->key)
        return SearchBST(bt->lchild,k);
    else
        return SearchBST(bt->rchild,k);
 }

三、二叉排序樹的循環算法實現

BSTNode *SearchBST1(BSTNode *bt, KeyType k)
 {
    while (bt!=NULL)
    {
        if(k==bt->key)
            return bt;
        else if(k<bt->key)
            bt=bt->lchild;
        else
            bt=bt->rchild;
     }
     return NULL;
  }

四、二叉排序樹的插入生成。

int InsertBST(BSTNode *&p,KeyType k)
 {
    if(p==NULL)
    {
        p=(BSTNode *)malloc(sizeof(BSTNode));
        p->key=k;
        p->lchild=p->rchild=NULL;
        return 1;

     }

     else if (k==p->key)
         return 0;
     else if (k<p->key)
         return InsertBST(p->lchild,k);
     else
         return InsertBST(p->rchild,k);
 }

五、二叉排序樹的刪除

int DeleteBST(BSTNode *&bt,KeyType k)
 {
    if(bt=NULL)
        return 0;
    else
    {
        if (k<bt->key)
            return DeleteBST(bt->lchild,k);
        else if(k>bt->key)
            return DeleteBST(bt->rchild,k);
        else
        {
            Delete(bt);
            return 1;
         }
     }
 }

 void Delete(BSTNode *&p)
 {
    BSTNode *q;
    if(p->rchild==NULL)
    {
        q=p;
        p=p->lchild;
        free(q);
     }
    else id(p->lchild==NULL)
    {
        q=p;
        p=p->lchild;
        free(q);
    }
    else
        Delete1(p,p->lchild);
 }

 void Delete1(BSTNode *p, BSTNode *&r)
 {
    BSTNode *q;
    if(r-rchild!=NULL)
        Delete1(p,r->rchild);
    else
    {
        p->key=r->key;
        q=r;
        r=r->lchild;
        free(q);
     }
 }

排序

一、排序定義

typedef int KeyType;     // 定義關鍵字類型
typedef struct         // 記錄類型
{
    KeyType key;  //關鍵字項
        InfoType data;  // 其餘數據項的類型InfoType
}RecType;            // 排序的記錄類型定義

二、直接插入排序

void InsertSort(RecType R[],int n)
 {
    int i,j;
    for (i=1,j<n;i++)
    {
        tmp=R[i];
        j=i-1;
        while (j>=0&&tmp.key<R[j].key)
        {
            R[j+1]=R[j];
            j--;    
         }
        R[j+1]=tmp;
       }
  }

三、使用折半查找插入排序

void InsertSort1(RecType R[],int n)
 {
    int i,j,low,high,mid;
    RecType tmp;
    for(i=1;i<n;i++)
    {
        tmp=R[i];
        low=0;
        high=i-1;

        // 用折半查找肯定插入位置

        while (low<=high)
        {
            mid=(low+high)/2;
            if(tmp.key<R[mid].key)
                high=mid-1;
            else
                low=mid+1;
         } 

         // 順序移動實施插入

         for(j=i-1;j>=high+1;j--)
             R[j+1]=R[j];
          R[high+1]=tmp; 
     }
 }

四、希爾排序

  • 先肯定一小於n的整數d1做爲第一個增量,把表的所有記錄分紅d1個組,全部距離爲d1的倍數的記錄放在同一個組中,在各組內進行直接插入排序。
  • 而後取出第二個增量d2(小於d1),重複上述的分組和排序,直至所取增量dt=1(d1>d2>d3..>dt-1>dt),即全部記錄放在同一組中進行直接插入排序爲止。

    void ShellSort(RecType R[],int n)
    {
    int i,j,gap,k;
    RecType tmp;
    gap=n/2;   // 增量置初值 
    while (gap>0) 
    {
        // 對全部相隔gap位置的全部元素組進行排序
         for (i=gap;i<n;i++)
         {
            tmp=R[i];
            j=i-gap;
            while (j>=0 && tmp.key<R[j].key)
            {
                P[j+gap]=R[j];
                j=j-gap;
             }
    
             R[j+gap]=tmp;
             j=j-gap;
          } 
    
        gap=gap/2;  //減少增量 
     }
    }

五、冒泡排序

  • 經過無序區中相鄰記錄關鍵字間額比較和位置交換,使關鍵字最小的記錄如氣泡通常逐漸往上「漂浮」直至「水面」。
  • n個元素排序,n-1趟冒泡。有序
void BubbleSort(RecType R[],int n)
 {
    int i,j ,k;
    RecType tmp;
    for(i=0;i<n-1;i++)
    {
        for(j=n-1;j>i;j--) // 一趟冒泡
            if(R[j].key < R[j-1].key)
            {
                tmp=R[j];
                R[j]=R[j-1];
                R[j-1]=tmp;
             } 
     }
  }

六、快速排序

void QuickSort(RecType R[],int t,int s)
 {
    int i=s, j=t;
    RecType tmp;
    if(s<t)
    {
        tmp=R[s]; //記錄基準

        // 進行一次劃分

        while(i!=j)
        {
            while(j>1&& R[j].key>=tmp.key)
            j--;
            R[i]=R[j];
            while (i<j && R[i].key<=tmp.key)
            i++;
            R[j]=R[i];
         } 

         R[i]=tmp;
         QuickSort(R,s,i-1);
         QuickSort(R,i+1,t); 
     }
  }

七、直接選擇排序 插入排序

void SelectSort(RecType R[],int n)
 {
    int i,j,k,l;
    RecType temp;
    for (i=0;i<n-1;i++)
    {
        k=i;  // 記錄無序區的起始位置

        // 在無序區選擇關鍵字最小的記錄,並記錄下標 
        for (i=i+1;j<n;j++)
            if(R[j].key<R[k].key)
                k=j;

        // 將關鍵字最小的記錄交換到無序區開始    
        if(k!=i)
        {
            temp=R[i];
            Rp[i]=R[k];
            R[k]=temp;
         } 
     }
 }

八、堆排序

* 堆排序實現原理:
    - 構造大根堆:從最後一個非葉子節點開始,構造大根堆(雙親節點的值大於其左右孩子節點),若是此節點區域其左右子樹均已成堆,將與「大」分支的根交換,直到成堆。 大者上浮,小者被篩選下去。
    - 將構造的大根堆,取出頭節點,此節點爲當前堆的最大值,而後將頭結點和最後的葉子節點交換位置,再次構造大根堆,且最後的葉子節點不參與大根堆得構建,依次循環選出從大到小的值。
// 構造初始堆

 void sift(RecType R[],int low,int high)    // low 和high 是樹的調整範圍
 {
    int i=low, j=2*i;  // R[j] 是R [i] 的左孩子
    RecType temp=R[i];
    while(j<=high)

    {
        if (j<high && R[j].key <R[j+1].key)
            j++; // 若右孩子較大, j指向右孩 2i+1 
        if(temp.key<R[j].key)
        {
            R[i]=R[j]; // 將R[j]調用到雙親節點位置上
            i=j;    // 修改i和j的值,以便繼續向下篩選
            j=2*i; 
        }
        else break;  // 篩選結束 
     } 
     R[i]=temp;  // 被篩選節點的值放入最終位置 

       } 

 // 進行堆排序 
 void HeapSort(RecType R[],int n)
 {
    int i;
    RecType temp;
    // 循環創建初始堆

    for (i=n/2;i>=1; i--)
        sift(R,i,n);

    // 選最大值置後並調整堆
    for (i=n;i>=2;i--)
    {
        temp=R[1];
        R[1]=R[i];
        R[i]=temp;
        sift(R,1,i-1);
     } 
  }

九、歸併排序

* 將兩個或兩個以上的有序表合併成一個新的有序表
void Merge(RecType R[],int low,int mid,int high)
 {
    RecType *R1;
    int i=low, j=mid+1,k=0;

    /* 動態分配空間R1,用於保存合併結果 */
    R1=(RecType *)malloc(high-low+1)*size(RecType));

  /** 兩段均未掃描時合併 **/
    while (i<=mid && 1<=high)
        if(R[i].key<=R[j].key)
        {
            R1[k]=R[i];
            i++;
            k++;

         } 
         else
         {
            R1[k]=R[j];
            j++;
            k++;
         }

    // 將第一段餘下的部分複製到R1
    while(i<=mid)
    {
        R1[k]=R[i];
        i++;
        k++;
     } 

    // 將第二段餘下的部分複製到R1

    while(j<=high)
    {
        R1[k]=R[j];
        i++;
        k++;
     } 

    // 將合併後的結果複製回R

    for(k=0,i=low;i<=high;k++,i++)
        R[i]=R1[k]; 
  } 

 void MergePass(RecType R[],int length,int n)
 {
    int i;
    for (i=0;i+2*length-1<n; i=i+2*length)
        Merge(R,i,i+length-1,i+2*length-1);
    if(i+length-1<n)
        Merge(R,i,i+length-1,n-1);

  } 

  void MergeSort(RecType R[],int n)
  {
    int length;
    for (length=1;length<n;length=2*length)
        MergePass(R,length,n);
  }

基數排序

一、基數排序的特色:

  • 不比較的排序:
  • 記錄R[i]的關鍵字R[i].key 是由d位數字組成,即k(d-1)K(d-2)...k(0),每個數字表示關鍵字的一位,每一位的值都在0<=k(i)<r 範圍內(r=10爲基數,表示用十進制)
    • 兩種基數排序
  • 最低位優先(LSD)
  • 最高位優先(MSD)
    • 經過「分配」和「收集」過程來實現排序(以最低位優先爲例)
      • 先按最低位的值對記錄進行分配、收集
      • 在前一趟的基礎上,再對高位的值分配和收集,直至最高位,則完成了基數排序的整個過程。
  • 基數排序是一種藉助於多關鍵字排序的思想對單關鍵字排序的方法。

二、基數排序的存儲結構和算法

#define MAXF 20  //線性表中的最多元素個數 
 #define MAXR 10  //基數的最大取值 
 #define MAXD 8   //關鍵字位數的最大取值

 //排序數據節點類型

 typedef struct node
 {
    char data[MAXD];
    struct node *next;
  } RecType1;
  RecType1 *p;
 // 用於分配和收集的隊列
 RecType1 *head[MAXR],*tail[MAXR] 

 void RadixSort(RecType *&p,int r, int d)
 {
    RecType *head[MAXR], *tail[MAXR],*t;
    int i,j,k;
    for(i=0;i<=d-1;i++) // 從低位到高位
    {
        // 初始化各鏈隊首和隊尾指針
        for(j=0;j<r;j++)
            head[j]=tail[j]=NULL; 

        //分配每個節點
        while(p!=NULL)
        {
            k=p->data[i]-'0';   // 將字符裝換爲數字 
            if(head[k]==NULL)   
            {
                head[k]=p;
                tail[k]=p;
             } 
             else
             {
                tail[k]->next=p;
                tail[k]=p;
             }
             p=p->next;
    } 

    p=NULL;
    for(j=0;j<r;j++)
        if(head[j]!=NULL)
        {
            if(p==NULL)
            {
                p=head[j];
                t=tail[j];
            }
            else
            {
                t->next=head[j];
                t->tail[j]; 
            }
         } 
    t->next=NULL;
 }

各類排序比較

一、平方階O(n^2)排序,通常稱爲加單排序,如直接插入,直接選擇和冒泡排序。
二、先行對數階O(nlog2n)排序,如快速,堆和歸併排序。
三、線性階O(n)排序,如基數排序。

數據結構基礎(二)

相關文章
相關標籤/搜索