Day 23.24 堆排序与归并排序

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,

堆排序

什么是堆?

堆一般都指的是二叉堆,它满足二个特性:

1父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2、每个结点的左子树和右子树都是一个二叉堆(都是大顶堆或小顶堆)。

如下为一个小顶堆(父结点的键值总是小于任何一个子节点的键值)

由此,若序列{k1k2,kn}是堆,则堆顶元素(或二叉树的根)必为序列中n个元素的最小值(或最大值)  若在输出堆顶的最小值之后使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。

堆的存储

为了利于存储和运算,一般堆采用完全二叉树表示。而完全二叉树又可以用数组来表示,因此堆可以使用数组来存储。根结点存在序号0

1i结点的双亲结点下标就为(i-1)/2;

2i结点的左右子结点下标分别为2*i+12*i+2

(注:如果根结点是从1开始,则左右孩子结点分别是2i2i+1。)

构造初始堆

构造初始堆

      建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。在数组中的话,我们一般从下标为(n-1)/2的数开始做堆调整,一直到下标为0的数(因为下标大于(n-1)/2的数都是叶子节点,其子树已经满足堆的性质了)

例如对数组(6,4,2,5,10,7,1,8,3,9)进行初始堆。

堆调整

当除根节点外其他所有结点都满足堆的要求时,对该根结点做堆调整,其实就是将该根节点进行下沉操作(具体是通过和子节点交换完成的)(筛选过程),一直下沉到合适的位置,使得树满足堆的性质。

  例如对大顶堆的堆调整我们会这么做:

1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个将其下标存储在largest中。

2、如果A[i]已经就是最大的元素则程序直接结束。

3、否则i的某个子结点为最大的元素A[largest]A[i]交换。

4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

相关代码:

#include <stdio.h>

#define MAX 10

 

 

void swap(ARR arr,int i,int j)

{

    ElementType temp = arr[i];

    arr[i] = arr[j];

    arr[j] = temp;

}

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

 

void heap(ARR arr); //堆排序

int main()

{

    ARR arr= {6,4,2,5,10,7,1,8,3,9};

heap(arr);

    return 0;

}

 

 

void heapadjust(ARR arr,int n,int m) //堆调整

{

    ElementType temp = arr[n];     //记录当前节点位置

    int i;          

    for(i = 2*n+1;i <= m;i = 2*i+1)

    {

        if(i < m && arr[i] < arr[i+1]) //比较左子树右子树

            i++;                     //如果右孩子元素大于左孩子则堆顶元素的值为右孩子

        if(arr[i] < temp)             //比较完后的孩子元素与节点比较

            break;

        arr[n] = arr[i];             //如果大于则该值赋值给节点

        n = i;

    }  

    arr[n] = temp;                    //当前节点的值用temp替换

}

 

void heap(ARR arr) //堆排序

{

    int i;

    for(i = (MAX-2)/2;i >= 0;i--) //初始化堆

        heapadjust(arr,i,MAX-1); //进行堆调整

    for(i = MAX - 1;i > 0;i--)

    {

        swap(arr,0,i);          //调整完后堆顶与最后一位元素(i)调换,完成后断开连接

        heapadjust(arr,0,i-1);  //再进行堆顶元素与第i-1个元素的堆调整

        print(arr);

   

                           

    }

}

 

归并排序

算法简介

   归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。

     将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

一、归并排序具体算法描述如下(递归版本)

1Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。

2Conquer: 对这两个子序列分别采用归并排序。

3Combine: 将两个排序好的子序列合并成一个最终的排序序列。

归并排序递归算法

 

二、非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

归并排序非递归算法

#include <stdio.h>

#define MAX 10

 

typedef int ElementType;

 

typedef ElementType ARR[MAX];

 

void print(ARR arr)

{

    int i;

    for(i = 0;i < MAX;i++)

    {

        printf("%d ",arr[i]);

    }

    printf("\n");

}

void merge(ARR arr,int left,int mid,int right); //进行归并

void mergesort1(ARR arr); //非递归的归并排序

void mergesort2(ARR arr,int left,int right); //递归的归并排序

 

int main()

{

   

    ARR arr = {6,4,2,5,10,7,1,8,3,9};

 

   

    mergesort1(arr);

    mergesort2(arr,0,MAX-1);

 

    return 0;

}

 

 

void merge(ARR arr,int left,int mid,int right) //进行归并

{

    int len = right - left + 1;

    ElementType temp[len];

    int k = 0;

 int i = left;                    // 前一数组的起始元素
int j = mid + 1;                // 后一数组的起始元素

    while(i <= mid && j <= right)

    {

        if(arr[i] < arr[j])

            temp[k++] = arr[i++];

        else

            temp[k++] = arr[j++];

    }                                

    while(i <= mid)

        temp[k++] = arr[i++];

    while(j <= right)

        temp[k++] = arr[j++];

    for(i = 0;i < len;i++)

        arr[left + i] = temp[i];

}

 

//归并排序

void mergesort1(ARR arr) //非递归的归并排序

{

    int left,right,mid;

    int i;

 for(i = 1;i < MAX;i *= 2) //i为步长每轮翻倍

    {

        left = 0;

     while(left + i < MAX) // 后一个子数组存在

        {

            mid = left + i - 1;

         right = (mid+i) < (MAX-1) ? (mid+i) : (MAX-1); // 后一个子数组大小可能不够

            merge(arr,left,mid,right);

         left = right + 1;                          前一个子数组向后移动

 

        }

        print(arr);

    }

 

void mergesort2(ARR arr,int left,int right) //递归的归并排序

{

    if(left == right)

        return;

    int mid = (left + right) / 2;

    mergesort2(arr,left,mid);

    mergesort2(arr,mid+1,right);

    merge(arr,left,mid,right);

    print(arr);

}

 

 

 

left,mid,right);

    print(arr);

}

 

 

 

猜你喜欢

转载自blog.csdn.net/j_xianyu/article/details/81546013