数据结构面试之十一——排序2(归并、快速、堆排序)

题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。

十、数据结构面试之十一——排序2(归并、快速、堆排序)

5. 归并排序

【算法思想】:采用分治法的算法思想,将原始数组分为A、B两个子数组,然后对A、B两个子数组继续划分为A1L,A1R,B1L,B1R四个子数组,继续划分直到数组中元素个数为1个时,即认为数组有序;然后再合并相邻的数组据可以。

核心分为3步骤:

第一,Divided,对应Divided()函数。

第二,Conquer,做相关处理。

第三,Merge,对应MergeArray()函数。

步骤示意图:

数组下标

a[0]

a[1]

a[2]

a[3]

a[4]

a[5]

a[6]

a[7]

a[8]

a[9]

元素值

9

8

7

6

5

4

3

2

1

0

Divided

1

first

 

 

 

mid

 

 

 

 

last

2

first

 

mid

 

last

 

 

 

 

 

3

first

mid

last

 

 

 

 

 

 

 

4

first&mid

last

 

 

 

 

 

 

 

 

 

Merge

5

first&mid

last

 

 

 

 

 

 

 

 

6

first

mid

last

 

 

 

 

 

 

 

 

Divided

7

 

 

 

first&mid

last

 

 

 

 

 

Merge

8

 

 

 

first&mid

last

 

 

 

 

 

Merge

9

first

 

mid

 

last

 

 

 

 

 

 

Divided

11

 

 

 

 

 

first

 

mid

 

last

12

 

 

 

 

 

first

mid

last

 

 

13

 

 

 

 

 

first&mid

last

 

 

 

 

Merge

14

 

 

 

 

 

first&mid

last

 

 

 

15

 

 

 

 

 

first

mid

last

 

 

 

Divided

16

 

 

 

 

 

 

 

 

first&mid

last

Merge

17

 

 

 

 

 

 

 

 

first&mid

last

Merge

18

 

 

 

 

 

first

 

mid

 

last

 

Merge

19

first

 

 

 

mid

 

 

 

 

last

 

【算法实现】:

template <typename T>
void MergeSort(T a[], int N)
{
       int*pTmpArray = new int[N]; //临时存储空间.转存用!
       if(pTmpArray== NULL)
       {
              cout<< "Allocate Error!" << endl;
       }
       intfirst = 0;
       intlast = N-1;
       Dirvied(a,first,last,pTmpArray);
       delete[]pTmpArray;
}
 
//合并数组
template <typename T>
void MergeArray(T a[], int first, int mid, int last, int tempArr[])
{    
       inti = first;
       intj = mid+1;
       intm = mid;
       intn = last;
       intk = 0;
 
       //两数组都非空
       while(i<= m && j <= n)
       {
              if(a[i]< a[j])
              {
                     tempArr[k++]= a[i++];
              }
              else
              {
                     tempArr[k++]= a[j++];
              }
       }
 
       //以下两循环代表有一个数组已空。
       while(i<= m)
       {
              tempArr[k++]= a[i++];
       }
       while(j<= n)
       {
              tempArr[k++]= a[j++];
       }
//临时存储的元素转存到a数组中.
       for(i = 0; i < k; i++)
       {
              a[first+i]= tempArr[i];  //注意此处的起点.
       }
}
 
//分解[递归函数]
template <typename T>
void Dirvied(T a[], int first, int last,int tempArr[])
{
       intmid;
       if(first< last)
       {
              mid= (first + last)/2;
              Dirvied(a,first,mid,tempArr);//左半部分.
              Dirvied(a,mid+1,last,tempArr);//右半部分.
              MergeArray(a,first,mid,last,tempArr);
       }
}


6. 快速排序

【算法思想】:采用分治法的算法思想,1)初始选定枢轴元素并记录枢轴元素和left,先从后向前遍历,小于枢轴的值就和left位置元素交换;然后从前向后遍历,大于枢轴的元素则与right位置元素交换,直到left>=right结束遍历。2)一次遍历后,就能保证枢轴左侧的元素小于枢轴值,枢轴右侧的元素大于枢轴值。记录下交换后枢轴所在的位置pivotPos。3)对pivotPos左侧的元素及pivot右侧的元素依次递归调用1)、2)即可。直至全部有序后结束。

 核心分为3步骤:

第一,Divided,对应Divided()函数。

第二,Conquer,做相关处理。

第三,Merge,每次Divide()后都能保证部分元素基本有序(大、小各在一侧)。

枢轴

0

1

2

3

4

5

6

7

8

9

 

72

6

57

88

60

42

83

73

48

85

pivot=72

<--48

 

 

<--42

 

left=right

 

 

88-->

 

 

<--48

6

57

<--42

60

72

83

73

88-->

85

 

 

 

 

 

 

 

 

 

 

 

pivot=48

48

6

57

42

60

 

 

 

 

 

 

<--42

 

left=right

57-->

60

 

 

 

 

 

 

<--42

6

48

57-->

60

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pivot=42

42

6

 

 

 

 

 

 

 

 

 

<--6

42

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pivot=57

 

 

 

57

60

 

 

 

 

 

 

 

 

 

57

60

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pivot=83

 

 

 

 

 

 

83

73

88-->

85

 

 

 

 

 

 

 

73

83

88-->

85

 

 

 

 

 

 

 

 

 

 

 

pivot=73

 

 

 

 

 

 

73

83

 

 

 

 

 

 

 

 

 

73

83

 

 

 

 

 

 

 

 

 

 

 

 

 

pivot=88

 

 

 

 

 

 

 

 

88

85

 

 

 

 

 

 

 

 

 

85

88

排序结果

6

42

48

57

60

72

73

83

85

88

 

【算法实现】:

//取枢轴,并按枢轴划分[左侧小于枢轴元素,右侧大于枢轴元素]
template <typename T>
int Partition(T a[], int left, int right)
{
       swap(a[left],a[(left+right)/2]);//交换,取中间元素为枢轴元素.
       T  pivot = a[left];             //暂存枢轴元素,用于比较
       while(left< right)
       {
              while(left< right && a[right] >= pivot)
              {
                     --right;
              }
              a[left]= a[right];           //[左]存储比枢轴元素小的值;
 
              while(left< right && a[left] <= pivot)
              {
                     ++left;
              }
              a[right]= a[left];           //[右]存储比枢轴元素大的值;
       }
       a[left]= pivot;                 //存放枢轴的新位置
       return left;
}
 
//递归函数
template <typename T>
void QuickCurve(T a[], int left, intright)
{
       intpivotPos = 0;
       if(left < right)
       {
              pivotPos= Partition (a,left,right); //获取枢轴分割后的位置,用以划分左右。
              QuickCurve(a,left,pivotPos-1);  //右边界-1
              QuickCurve(a,pivotPos+1,right);//左边界+1
       }
}
 
//快排
template <typename T>
void QuickSort(T a[], int N)
{
       QuickCurve(a,0,N-1);//left=0, right=N-1
}
 


7.堆排序

【算法引出】:是简单选择排序算法的改进算法,简单选择排序中每一趟比较选出一个最小值,但是后一趟的比较中会重复前面的比较结果,存在重复。堆排序对其的改进体现在—— 每次选择最小值的同时,根据结果对其他的值进行调整。

【堆的概念】:堆是具有以下性质的完全二叉树,每个节点都大于或等于左右孩子节点的值,称为大顶堆;每个节点都小于等于左右孩子节点的值,称为小顶堆。

【算法思想】:以小顶端堆为例,(1)首先构造一个小顶堆,即堆顶为所有元素的最小值;(2)其次,将该堆顶元素与堆末尾元素互换,此时堆末尾便存储了最小元素;(3)除去堆末尾元素,对于剩余的N-i个元素反复执行(1)、(2)操作即可完成排序。

【算法实现】:

//构建与调整小顶堆.

//大顶堆的调整方法同小顶堆,所做的改变只是比较符号的变换;

//arr[j+1] > arr[j],arr[j] <= temp

template<typename T>
void heapAdjust(T arr[], int i, int N)
{
       intj;
       inttemp = arr[i];    //临时存储需要节点信息.
       j= i*2+1;         //左孩子节点
 
       while(j< N)
       {
              if(j+1< N && arr[j+1] < arr[j])
              {
                     j++;                //取左右孩子中的小值.
              }
              if(arr[j]>= temp)
              {
                     break;             //小顶堆,如果出现孩子节点值大,则终止循环.
              }
              //找寻是否存在孩子节点的孩子节点.
              arr[i]= arr[j];
              i= j;
              j= 2*i + 1;
       }//endwhile
       arr[i]= temp; //存储新的位置.
}
 
//堆排序.
template<typename T>
void heapSort(T arr[], int N)
{
       //构建小顶堆,初始是凌乱的,调整后成一个小顶堆
       for(int i = N-1; i >= 0; i--)
       {
              heapAdjust(arr,i/2,N-1);//比较的位置从中间开始.
       }
 
       for(int  i = N-1; i >= 0; i--)
       {
              swap(arr[i],arr[0]);
              heapAdjust(arr,0,i);  //终止的大小为i,每次只调整根节点即可。
       }
}



发布了359 篇原创文章 · 获赞 2735 · 访问量 329万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503

分享到微信朋友圈

×

扫一扫,手机浏览