各种排序算法python和java实现(二)

第一篇博客实现了三种最基本最简单的排序算法,本篇文章将在这三种算法的基础上稍微演变一下。

1.快排

光从名字看就知道速度肯定不差,前一篇讲的冒泡排序,怎么看都不算是一种好的排序算法,里面充斥了太多的无谓的交换动作,时间复杂度倒是很稳定o(n^2),但对于排序算法实在说不过去。快排是冒泡排序的改进版,思路就是分治,将一个序列随机按照某个值分成两个子序列,子序列A里面的值全部比该值大,另一个子序列B的值全部比该值小,这听起来像是二叉排序树。然后依次对子序列进行如上操作,很明显快排最简单的实现就是用递归。

看下java实现的快排:

/**
	 * 快速排序,最快O(nlogn) 最差O(n^2)
	 * 
	 * @param low
	 * @param high
	 * @param array
	 */
	public static void quickSort(int low, int high, int[] array) {
		if (low >= high) {
			return;
		}
		int i = low;
		int j = high;
		// 此处选取的中轴为当前i和j的中间值
		int m = (i + j) / 2;
		int temp = array[m];
		array[m] = array[high];
		array[high] = temp;

		// 中轴
		int pivotkey = array[high];
		while (i < j) {
			while (i < j && array[i] <= pivotkey) {
				i++;
			}
			array[j] = array[i];

			while (i < j && array[j] >= pivotkey) {
				j--;
			}
			array[i] = array[j];
		}
		// 此时i和j相等,替换当前i的值
		array[i] = pivotkey;
		
		//对左子集做快排
		quickSort(low, i - 1, array);
		//对右子集做快排
		quickSort(i + 1, high, array);
	}

再来看下python的实现:

def quickSort(low,high,array):
	if low > high:
		return
	i ,j = low,high
	m = (i+j)/2
	temp = array[m]
	array[m] = array[high]
	array[high] = temp

	pivotkey = array[high]

	while i < j:
		while i < j and array[i] < pivotkey:
			i+=1
		array[j] = array[i]
		while i < j and array[j] >= pivotkey:
			j-=1
		array[i] = array[j]
	array[i] = pivotkey
	quickSort(low,i-1,array)
	quickSort(i+1,high,array)
没有了各种大括号看起来挺顺眼的。
2.各种直接插入排序的版本。
其实直接插入排序的主要的问题就是找到带插入值的位置,既然子集已经有序,那么不妨使用比较快速的方法,二分查找来完成index的查找。直接看代码:
/**
	 * 二分查找index的插入排序
	 * @param array
	 */
	public static void insertSortBinary(int[] array)
	{
		for(int i=1;i<array.length;i++)
		{
			int temp = array[i];
			int index = binarySearch(0, i, array, array[i]);
			for(int j=i;j>index;j--)
			{
				array[j] = array[j-1];
			}
			array[index] = temp;
		}
	}
	/**
	 * 二分查找微改版
	 * @param start
	 * @param end
	 * @param array
	 */
	public static int binarySearch(int start,int end,int[] array,int value)
	{
		while(start <= end)
		{
			int mid = (start + end)/2;
			if(array[mid] == value)
			{
				return mid;
			}
			else if(array[mid] < value)
			{
				start = mid + 1;
			}
			else
			{
				end = mid - 1;
			}
		}
		return start;
	}
python实现:
def insertSortBinary(array):
	for i in xrange(1,len(array)):
		index = binarySerach(array,array[i])
		for j in range(index,i)[::-1]:
			array[j] = array[j-1]
		array[i],array[index] = array[index],array[i]

def binarySerach(array,value):
	low,high = 0,len(array)-1
	while low <= high:
		mid = (low + high)/2
		if value == array[mid]:
			return mid
		elif value < array[mid] :
			high = mid -1
		else :
			low = mid + 1
	return low
希尔排序也是一种插入排序,只不过它先让整体的序列保持部分有序,这样可以加快排序速度,java实现:
/**
	 * 希尔排序,步长g 直接插入排序如果部分有序则速度明显提升,因此考虑 使用分治法,先让子集有序,然后在插入这里有个步长的概念,
	 * 即这个步长内的子集有序,逐步扩大步长,从而做到整体有序
	 */
	public static void shellSort(int[] array) {
		int[] gs = {5,4,3,2,1};
		for(int i=0;i<gs.length;i++)
		{
			shellInsert(array, gs[i]);
		}

	}
	/**
	 * 希尔插入,只不过多了步长g
	 * 
	 * @param array
	 * @param g
	 */
	public static void shellInsert(int[] array, int g) {
		int temp,j;
		for (int i = g; i < array.length; i += g) {
			temp = array[i];
			for (j = i - g; j >= 0; j-=g) {
				if (temp < array[j]) {
					array[j + g] = array[j];
				} else {
					break;
				}
			}
			array[j+g] = temp;
		}
	}
python实现:
def shellInsert(array,g):
	for i in range(g,len(array),g):
		if array[i] < array[i-g]:
			temp = array[i]
			for j in range(0,i,g)[::-1]:
				if temp < array[j]:
					array[j+g] = array[j]
				else:
					j+=g
					break
			array[j] = temp


def shellSort(array):
	gap = [5,4,3,2,1]
	for x in range(1,6)[::-1]:
		shellInsert(array,x)



3.基于简单的选择排序,可以衍生出更复杂的堆排序,回顾一下简单排序就是每次选出第i大或者第i小的值的过程,实际上堆的特性很好的符合这个特性,先来看下堆是什么?
堆有最大堆,最小堆,只是从小到大,从大到小的区别,首先堆是一种数据结构,是一棵完全二叉树且满足性质:所有非叶子结点的值均不大于或均不小于其左、右孩子结点的值(看着和二叉搜索树定义有些类似),如下是一个堆的示例:

针对堆的特点,像有序的堆中加入一个元素速度是很快的,因为查找要插入的位置速度要快的多,既然堆是一种数据结构,那么会使用什么结构来存储呢,通常像这种比较复杂的结构都是链表实现,其实用数组来实现堆是很方便的,堆排序的过程其实就是构建一个堆的过程,这里以构建一个最大堆为例子,既然堆是一棵完全二叉树,不妨从堆定开始看,依次从左到右按照数组排序比如上面的树可以表示为{9,8,5,3,1,2},假设将树中某个非叶子节点看做下标为i,则可以有key[i]>=key[2i+1] && key[i]>=key[2i+2]这里的i从0开始。创建堆得过程实际上就是不断调整堆得过程,先来看下堆调整的思路:

懒得画图了,直接使用别人画好的图,将i和2i+1,2i+2三个元素中最大的值和i交换,然后依次将被交换的值当做i来处理,这里可以使用递归的形式或者非递归(找准结束)条件直到i为叶子节点。看java的实现:
/**
	 * 调整某个子堆,从i节点开始从上到下调整。 将最大值调整到堆顶
	 * 
	 * @param array
	 * @param i
	 */
	public static void adjustHeap(int[] array, int i, int len) {
		int maxIndex = i;
		// i为叶子及节点
		if ((2 * i + 1) >= len) {
			return;
		}
		// 肯定有左孩子
		else {
			// 左孩子大于父亲
			if (array[i] < array[2 * i + 1]) {
				maxIndex = 2 * i + 1;
				// 有右孩子
			}
			if ((2 * i + 2) < len) {
				System.out.println("have right");
				if (array[maxIndex] < array[2 * i + 2]) {
					maxIndex = 2 * i + 2;
				}
			}
		}
		// 交换
		if (maxIndex != i) {
			int temp = array[i];
			array[i] = array[maxIndex];
			array[maxIndex] = temp;

			// 递归调整
			adjustHeap(array, maxIndex, len);
		}
	}

	/**
	 * 堆的建立过程,是从底像上不断调整的过程,最先开始调整的是最后一个非叶子节点
	 * 
	 * @param array
	 */
	public static void createHeap(int[] array) {
		if (array.length <= 1) {
			return;
		}
		// 从最后一个非叶子节点开始调整
		for (int i = array.length / 2 - 1; i >= 0; i--) {
			adjustHeap(array, i, array.length);
		}
	}

	public static void heapSort(int array[]) {
		createHeap(array);
		for (int i = array.length - 1; i >= 0; i--) {
			int temp = array[0];
			array[0] = array[i];
			array[i] = temp;
			adjustHeap(array, 0, i);
		}
	}

调整堆得过程执行时间和堆得高度h相关,因此时间为O(logn),而堆的建立过程是个n为基础的循环所以,建立整个堆的时间为O(nlogn)。
python实现:
def heapSort(array):
	buildHeap(array)
	for x in xrange(0,len(array)):
		array[0],array[len(array)-x-1] = array[len(array)-x-1],array[0]
		adjustHeap(array,len(array)-x-1,0)
#from last no leave node
def buildHeap(array):
	x = len(array)/2 - 1
	while x>=0:
		adjustHeap(array,len(array)-x,x)
		x-=1
#from top to down
def adjustHeap(array,size,root):
	maxIndex = root
	if (2*root + 1) >= size:
		return
	if array[2*root+1] > array[maxIndex]:
		maxIndex = 2*root + 1
	if (2*root+2) < size:
		if array[2*root+2] > array[maxIndex]:
			maxIndex = 2*root+2
	if maxIndex!=root:
		array[maxIndex],array[root] = array[root],array[maxIndex]
		adjustHeap(array,size,maxIndex)


4.归并排序
归并排序是一种典型的分治思想将一个大的集合看做两个有序的集合,在合并两个子集的时候,形成一个大的有序集合,最后分治到两个子集各一个元素,看代码:
 /**
     * 归并排序,递归调用
     * @param array
     * @param start
     * @param end
     */
    public static void mergeSort(int[] array,int start,int end)
    {
        if(start >= end)
        {
            return;
        }
        int mid = (start + end)/2;
        mergeSort(array, start, mid);
        mergeSort(array,mid + 1,end);

        merge(array,start,end,mid);
    }

    /**
     * merge方法,合并两个有序的子集
     * @param array
     * @param start
     * @param end
     */
    public static void merge(int[] array,int start,int end,int mid)
    {
        int[] temp = new int[end - start + 1];
        int i = start;
        int j = mid +1;
        int k = 0;

        while (i <= mid && j<=end)
        {
            if(array[i] < array[j])
            {
                temp[k++] = array[i++];
            }
            else
            {
                temp[k++] = array[j++];
            }
        }
        //拷贝剩余的值到temp
        while (i<=mid)
        {
            temp[k++] = array[i++];
        }

        while (j<=end)
        {
            temp[k++] = array[j++];
        }

        System.arraycopy(temp,0,array,start,end - start +1);

    }
python实现:
def mergeSort(array,low,high):
	if low>=high:
		return
	mid = (low+high)/2
	mergeSort(array,low,mid)
	mergeSort(array,mid+1,high)
	merge(array,low,high,mid)
#merge two list
def merge(array,low,high,mid):
	temp_array = []
	i,j = low,mid+1
	while i<=mid and j<=high:
		if array[i]<array[j]:
			temp_array.append(array[i])
			i+=1
		else:
			temp_array.append(array[j])
			j+=1
	while i<=mid:
		temp_array.append(array[i])
		i+=1
	while j<=high:
		temp_array.append(array[j])
		j+=1
	for x in xrange(0,len(temp_array)):
		array[low] = temp_array[x]
		low+=1

最近这段时间在出差,基本没有时间写博客了,不过还是挤出一点儿时间完成它,不然有些东西没完成,心里总是不舒服。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章