二叉树

二叉树的性质

1、非空二叉树上的叶节点数等于双分支节点数加1.
2、非空二叉树上第i层上至多有2^(i-1)个节点,这里应有1>=1.
3、高度为h的二叉树至多有2^h - 1 个节点 (h>=1)。
4、在二叉树中,如果所有分支节点都有左孩子和右孩子节点,并且叶子节点都集中在二叉树的最下一层,这样的二叉树称为满二叉树。

  • 只有度为0和度为2的节点
  • 叶子节点都在最下一层

5、完全二叉树:二叉树中最多只有最下面两层的节点的度数可以小于2,并且最下面一层的叶子节点都依次排列在该层最左边的位置上,则这样的二叉树称为完全二叉树。

  • 叶子节点只能在层次最大的两层上出现
  • 最大层次上的叶子节点,都依次排列在该层最左边的位置上
  • 如果有度为1的节点,只能有一个,并且该节点只有左孩子,没有右孩子。

二叉树的链式存储结构

二叉树的基本运算主要有以下几种:

  • 创建二叉树 : CreateBTNode(b,str)
  • 查找节点: FindNode(*b,x)
  • 找孩子节点:LchildNode(p)和Rchild-Node(p)
  • 求高度:BTNodeDepth(*b)
  • 输出二叉树:DispBTNode(*b)

1、二叉树的定义

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

2、使用字符数组构造二叉树

 // 根据字符串构造二叉树
 // 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];
     }
  } 

3、查找节点

  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);
      }
  }

4、 找孩子节点

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

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

5、求高度

 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); 
    } 
  }

6、输出二叉树

 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(")");
          }
      }
  }

7、二叉树的前序中序,和后序遍历(递归操作):

// 前序遍历
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);
      }
  }

8、先序遍历的非递归操作,使用栈:

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;
        }
      }
  }

9、中序遍历

  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;
         } 
      }
  }

10、后序遍历

 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);
  } 

1、有向图

  • 边之间的“顶点对”是无序的,则称为G为无向图。
  • (i,j)表示一条无向边,和(j,i)是同一条边。

2、有向图

  • 边之间的顶点对是有序的,则称G为有向图。
  • <i,j> 表示由i到j方向有一条边。

3、图的数据操作

  • 初始化图InitGraph(&g):构造一个空的图g
  • 销毁树ClearGraph(&g):释放图g占用的内存空间
  • DFS(G,v): 从顶点v出发,深度优先遍历图g
  • BFS(G,v):从顶点v出发,广度优先遍历图g

4、端点和邻接点

  • 在一个无向图中,若存在一条边(i,j)
    • 称顶点i和顶点j为此边的两个端点
    • 称顶点i和顶点j互为邻接点
  • 在一个有向图中,若存在一条边<i,j>
    • 称边<i,j>是顶点i的一条出边,同时也是顶点j的一条入边。
    • 称i为此边的起始端点(简称为起点),顶点j为终止端点(简称终点);
    • 称顶点i和顶点j 互为邻接点。

5、顶点的度

  • 在无向图中
    • 顶点所具有的边的数目称为该顶点的度。
  • 在有向图中
    • 以顶点i 为终点的入边的数目,称为该顶点的入度。
    • 以顶点i 为始节点的出边的数目,称为该顶点的出度。
    • 一个顶点的入度和出度的和为该顶点的度
      • 若一个图中有n个顶点和e条边,每个顶点的度为di (1<=i<=n), 有 e= 1/2∑di

6、完全图

  • 无向图
    • 无向图中的每两个顶点之间都存在着一条边,则称此图为无向完全图。
    • 完全无向图包含有n(n-1)/2条边。
      • 有向图
    • 有向图中的每两个顶点之间都存在着方向相反的两条边,则称此图为完全有向图。

7、邻接矩阵的存储方法

  • 图的邻接矩阵表示是唯一的
  • 邻接矩阵的存储
    • 无向图的邻接矩阵一定是一个对称矩阵,可以考虑压缩存储
    • 不少邻接矩阵是一个稀疏矩阵,当涂的顶点较多时,可以采用三元组的方法存储。
  • 顶点的度
    • 对于无向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好使第i个顶点的度。
    • 对于有向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好使第i个顶点的出度或入度。
  • 性能
    • 用邻接矩阵方法存储图,很容易确定图中任意两个顶点之间是否有边相连。
    • 要确定图中有多少条边,则必须按行,按列队每个元素进行检测,时间代价大。

1、数据类型的定义

#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;

2、 使用邻接表创建图,并显示,使用一个二维数组表示邻接矩阵:

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;
 }

3、邻接表特点

  • 顺序分配与链式分配相结合
  • 每个顶点建立一个单链表
  • 第j个单链表中的节点表示依附于顶点j的边
    • 每个单链表上附设一个表头节点
    • 对有向图,是以顶点j为尾的边。
  • 邻接表表示不唯一:在每个顶点对应的单链表中,各边节点的连接此时可以使任意的。
  • 可能空间耗费大:对于有n个顶点节点和e条边的无向图,其邻接表有n个顶点节点和2e个边节点。
  • 对于无向图,邻接表的顶点i对应的第i个链表的边节点数目正好是顶点i的度。
  • 对于有向图,邻接表的顶点i对应的第i个链表的边节点数目仅仅是顶点i的出度;其入度为邻接表中所有asjvex域值为i的边节点数目。

4、邻接表存储有权图方法,使用顺序表存储顶点,链表存储边

*  表头节点:
    - 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; // 完整的图邻接表类型  

5、 将邻接矩阵转换为邻接表

 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;
   } 

6、 将邻接表装换为邻接矩阵

  // 初始 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;
          }
      }
  }

7、图的遍历

* 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"); 
   } 

8、非连通图的遍历

  // 深度优先搜索遍历非连通无向图
  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);
    } 

查找

线性表的顺序查找

1、存储结构定义:

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

2、顺序查找

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;
}

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

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;
}

2、 使用递归算法实现

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);

3、折半查找的扩展-分块查找

* 将要查询的数据域分成等量的数据域,对每个区域的最大值建立索引,每个区域的数据可以使无序的,当时数据域中的整体数据应该比上一个数据域的所有数据都大(或都小),这样对建立的索引进行折半查找,查找后找到对应的数据域,在对数据域进行顺序查找。
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;
 } 

二叉排序树

1、二叉排序树(BST)的特性

* 若他的左子树非空。则左子树上所有记录的值均小于根记录的值。
* 若它的右子树非空,则右子树上所有记录的值均大于根记录的值
* 左右子树本身又各是一棵二叉排序树。

2、二叉排序树的递归算法实现

 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);
 }

3、二叉排序树的循环算法实现

 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;
  } 

4、二叉排序树的插入生成。

 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);
 }

5、二叉排序树的删除

 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);
     }
 }

排序

1、排序定义

typedef int KeyType;     // 定义关键字类型
typedef struct         // 记录类型
{
    KeyType key;  //关键字项
        InfoType data;  // 其他数据项的类型InfoType
}RecType;            // 排序的记录类型定义

2、直接插入排序

 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;
       }
  }

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

 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; 
     }
 }

4、希尔排序

  • 先确定一小于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;  //减小增量 
     }
    } 
    

5、冒泡排序

  • 通过无序区中相邻记录关键字间额比较和位置交换,使关键字最小的记录如气泡一般逐渐往上“漂浮”直至“水面”。
  • 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;
             } 
     }
  } 

6、快速排序

 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); 
     }
  } 

7、直接选择排序 插入排序

 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;
         } 
     }
 }

8、堆排序

* 堆排序实现原理:
    - 构造大根堆:从最后一个非叶子节点开始,构造大根堆(双亲节点的值大于其左右孩子节点),如果此节点区域其左右子树均已成堆,将与“大”分支的根交换,直到成堆。 大者上浮,小者被筛选下去。
    - 将构造的大根堆,取出头节点,此节点为当前堆的最大值,然后将头结点和最后的叶子节点交换位置,再次构造大根堆,且最后的叶子节点不参与大根堆得构建,依次循环选出从大到小的值。
// 构造初始堆

 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);
     } 
  } 

9、归并排序

* 将两个或两个以上的有序表合并成一个新的有序表

 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);
  }

基数排序

1、基数排序的特点:

  • 不比较的排序:
  • 记录R[i]的关键字R[i].key 是由d位数字组成,即k(d-1)K(d-2)...k(0),每一个数字表示关键字的一位,每一位的值都在0<=k(i)<r 范围内(r=10为基数,表示用十进制)
    • 两种基数排序
  • 最低位优先(LSD)
  • 最高位优先(MSD)
    • 通过“分配”和“收集”过程来实现排序(以最低位优先为例)
      • 先按最低位的值对记录进行分配、收集
      • 在前一趟的基础上,再对高位的值分配和收集,直至最高位,则完成了基数排序的整个过程。
  • 基数排序是一种借助于多关键字排序的思想对单关键字排序的方法。

2、基数排序的存储结构和算法

 #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;
 }

各种排序比较

1、平方阶O(n^2)排序,一般称为加单排序,如直接插入,直接选择和冒泡排序。
2、先行对数阶O(nlog2n)排序,如快速,堆和归并排序。
3、线性阶O(n)排序,如基数排序。

数据结构基础(二)