• 详解折半插入排序算法


    折半插入排序算法的时间复杂度:O(nlogn)

    折半插入排序利用二分法的思想,在一个有序的序列中,找到新元素在该序列中的位置,然后插入。如图1所示,共有n个元素,前i个元素已经是有序序列,现在要将第i个元素插入其中。折半插入排序需要做两步工作:找到待插入元素的位置、插入。


    图1 插入排序示意图


    首先要定义两个指针(不是语法里面的指针,是下标的意思)low和high用于寻找a[i]的插入位置,low指向a[0],high指向a[i-1],中点mid=(low+high)/2,


    图2 “折半”示意图


    如图2所示二分法的思想是,比较a[i]与a[mid]的大小,若a[i]>a[mid],说明a[i]的位置应该在mid~high之间,将区间[low,high]缩短为[mid+1,high],令指针low=mid+1;若a[i]<=a[mid],说明a[i]的位置应该在low~mid之间,将区间压缩为[low,mid-1],令指针high=mid-1。每次折半之后,a[i]的位置应该在[low,high]之间。

    如此循环,low与high渐渐靠近,直到low>high跳出循环,a[i]的位置找到,low即为a[i]应该放置的位置。

    找到a[i]的位置之后进行插入,先将a[low]~a[i-1]这些元素向后平移一个元素的位置,然后将a[i]放到low位置。


    用Dev-C++编写的C++代码:


    #include <iostream>
    using namespace std;
    
    void BinSort(int *a,int n) //对int数组进行从小到大的排序 
    {
    	for(int i=1;i<n;i++) //开始 以a[0]作为有序序列,从a[1]开始找到当前元素a[i]应该放置的位置 
    	{
    		int low=0,high = i-1,mid;//每次寻找a[i]的位置,都要更新这些数据 
    		while(low<=high) //二分思想循环寻找a[i]的位置 
    		{
    			mid = (low+high) / 2;  
    			if(a[i]<=a[mid])
    				high = mid - 1;  //high指针减小 
    			else
    				low = mid + 1;   //low指针增加 
    		}  //循环结束,low就是a[i]应该放置的位置 
    		
    		int temp = a[i];  
    		for(int j=i;j>low;j--)  //将元素向后平移
    			a[j] = a[j-1];  
    		a[low] = temp;   //将元素temp = a[i] 放置到low位置 
    	}
    }
    
    int main()  //举例说明
    {
    	int n = 10;
    	int a[10] = {5,8,9,4,7,5,6,3,1,11}; 
    	BinSort(a,n);
    	for(int i=0;i<n;i++)
    		cout << a[i] << " ";
    	cout << endl;
    	
    	return 0;
    }

    一个细节:为什么内层的循环while(low<=high){...}结束之后以low作为a[i]应该放置的位置?

    观察指针low与high重合之前与之后二者位置变化情况。设low>high的一步为第N步,走到第N步一共有四种情形:


    情形一:


    第N-2步时,mid=(low+high)/2,如果a[i]>a[mid],low=mid+1,这样就到了第N-1步,low与high重合,mid=(low+high)/2=low=high,由于a[i]<=a[high]=a[mid]必然成立(否则,a[i]>a[mid]=a[high],那么a[i]放置的位置应该在high之后了,也不会到达这一步),所以high=mid-1,这样就到了第N步,high<low,退出循环,此时位置high就是第N-2步的位置mid,所以此时,a[high]<a[i]<=a[low],所以low就是a[i]应该放置的位置。


    情形二:


    第N-2步,mid=(low+high)/2,如果a[i]<=a[mid],high=mid-1,这样就到了第N-1步,low与high重合,mid=(low+high)/2=low=high,由于a[i]>a[low]=a[mid],所以low=mid+1,这样就到了第N步,high<low,退出循环,此时位置low就是第N-2步的位置mid,那么此时,a[high]<a[i]<=a[low],所以low就是a[i]应该放置的位置。


    情形三:


    第N-2步,mid=(low+high)/2=low,如果a[i]>a[mid]=a[low],low=mid+1,这样就到了第N-1步,low与high重合,mid=low=high,由于a[i]<=a[high]=a[mid],所以,high=mid-1,这样就到了第三步,high<low,退出循环,此时位置high就是第N-2步的位置low,那么此时,a[high]<a[i]<=a[low],所以low就是a[i]应该放置的位置。


    情形四:


    第N-1步,mid=(low+high)/2 = low,如果a[i]<=a[mid]=a[low],high=mid-1=low-1,这样就到了第N步,low>high,退出循环,此时a[high]<a[i]<=a[low],所以low就是a[i]应该放置的位置。

    这样就解释了为什么退出循环之后,将low作为寻找的位置。


    如有错误请指正!

  • 相关阅读:
    超越自我的事
    ENVI/SARscape软件概述及安装
    《万万没想到:用理工科思维理解世界》读书简记
    Ruby on Rails 搭建环境 (ubuntu)
    拓荒者
    Rails 画类图的几个方法
    ERROR: Error installing mysql2: ERROR: Failed to build gem native extension [@Ubuntu 15.04]
    无题20150105
    关于Unity中Camera的Aspect
    Esfog_UnityShader教程_逐帧动画
  • 原文地址:https://www.cnblogs.com/tensory/p/6590799.html
Copyright © 2020-2023  润新知