Loading

经典排序算法 - 插入排序&希尔排序

插入排序

插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。

插入排序方法分直接插入排序和折半插入排序两种,这里只介绍直接插入排序,折半插入排序留到“查找”内容中进行。

题目:给出无需数组 [4,3,1,2],要求按照从小到大使用插入排序法排序。
输出样例:

1
2
3
4

解题思路:

  1. 先固定住第一个元素,第二个元素与第一个元素比较,若小于第一个元素则交换位置;
  2. 将第三个元素与前面2个元素依次比较,若小于则插入被比较的元素前面;
  3. 将第四个元素与前面3个元素依次比较,若小于则插入被比较的元素前面。

/**
 * 插入排序
 * 时间O(N^2),空间O(1)
 * 原理:把数组分成已排序和未排序两个区间,以数组第一个元素当做已排序区间,剩下的即被当做未排序区间,每次都从未排序区间中找出一个元素来和已排序区间中的元素比较,并插入到已排序区间中的合适位置(如果发生了后移),直到未排序区间元素为 0 。注意,该过程中:为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。
 *
 * https://www.cnblogs.com/kkun/archive/2011/11/23/2260265.html
 * https://www.jianshu.com/p/6d55ac4b72b4
 * @return array
 */
function InsertSort($arr)
{
    $len = count($arr);

    //预处理: 处理1,2,3,4这种情况
    $exchange = 0;
    for ($i = 1; $i< $len; $i ++) {
        if ($arr[$i] < $arr[$i - 1]) {
            $temp = $arr[$i];
            $arr[$i] = $arr[$i - 1];
            $arr[$i - 1] = $temp;
            $exchange ++;
        }
    }
    if ($exchange == 0) { //若交换次数为0(即数组有序),则无需进行下一步排序。
        return $arr;
    }

    //插入排序开始
    //先遍历未排序区间元素,从第二个元素开始。第一个元素当做已排序
    for ($i = 1; $i< $len; $i ++) {
        $tmp = $arr[$i]; //获取未排序区间中的第一个元素,那么这个位置可以认为是空出来的,用于后面移到元素

        //遍历已排序区间元素,从尾部开始
        $j = $i; //j指向i空出来的位置
        while ($j > 0 &&  $tmp < $arr[$j - 1]) {//拿未排序区间中的第一个元素和已排序区间中的元素依次比较,直到找到适合插入的位置
            $arr[$j] = $arr[$j-1]; //往后移动元素,留出插入位置。因为j的位置本来就是空的,不会导致覆盖
            $j --; //j指向往前(左)挪一步。由于上一步发生了后移, 此时j指向的位置是空的
        }

        // 如果发生了移动,那么j和i肯定不同,此时的j已经指向的空的位置,而且本轮不需要再移到,需要把开始放在temp里的元素放到当前空的位置
        if ($j != $i) {
            $arr[$j] = $tmp;//将未排序区间拿出来的元素插入到合适的位置
        }
    }

    return $arr;
}

3 1 2 4 为例:
第1轮: 已排序是3。将待排序的第一个元素也就是1放到temp暂存,j指向空位置,示意图: 3 x 2 4。1与已排序3比较,小于3,3往后挪到空位置,将j往前移一步(j--),重新指到空位置,这时候本轮结束,将temp值放回j处。示意图: 1 3 2 4。
第2轮: 已排序是1,3。将待排序2放到temp暂存,j指向空位置,示意图: 1 3 x 4。2<3,3后挪到空位置,将j往前移一步(j--),示意图: 1 x 3 4; 2>1,循环停止。将temp的值2放到空位置j处, 示意图: 1 2 3 4。
第3轮: 已排序是1,2,3。将待排序4放到temp暂存,j指向空位置,示意图: 1 2 3 x。4分别于3,2,1比较均大于,无需后移。由于未发生后移,最后一个元素还是4。
结束。

go示例:

//插入排序
//时间复杂度O(N^2),空间复杂度O(1)
//https://www.cnblogs.com/ghj1976/archive/2013/02/28/2937029.html
func InsertSort(arr []int) {
	length := len(arr)

	for i := 1; i < length; i++ {
		temp := arr[i] //暂存未排序区间第一个元素
		j := i         //j指针指向未排序第一个

		for j > 0 && temp < arr[j-1] { //temp值更小
			arr[j] = arr[j-1] //元素后移
			j--               //指针迁移,继续比较前一个已排序数
		}

		//判断是否发生后移,如果有说明未排序区存在更小的值
		if j != i {
			arr[j] = temp
		}
	}
}

希尔排序

希尔排序是对插入排序的一种改进,将增量由1变成了>=1的数。下面是Go的版本,可以对比上面插入排序Go版本看一下。

package main
import "fmt"

func ShellSort(arr []int){
	l := len(arr)
	incr := l-1

	for incr > 0 {
		//insertSort
		for i:=incr; i<l; i++ {
			tmp := arr[i]
			j:=i 
			for j >0 && arr[j-incr] > tmp {
				arr[j] = arr[j-incr]
				j=j-incr
			}
			arr[j] = tmp
		}

		incr = incr / 2
	}
}

func main(){
	arr := []int{10,2,3,87,3,11}
	ShellSort(arr)
	fmt.Println(arr)
}

运行:


go run main.go
[2 3 3 10 11 87]

参考:
经典排序算法 – 插入排序Insertion sort - kkun - 博客园
http://www.cnblogs.com/kkun/archive/2011/11/23/2260265.html

posted @ 2017-07-22 21:54  飞鸿影  阅读(266)  评论(0编辑  收藏  举报