各種排序算法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

最近這段時間在出差,基本沒有時間寫博客了,不過還是擠出一點兒時間完成它,不然有些東西沒完成,心裏總是不舒服。

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