直接选择、插入排序
直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是直接插入排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。
直接选择排序 class Solution: # @param {int[]} A an integer array # @return nothing def sortIntegers(self, A): # Write your code here # 直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。 length = len(A) for i in xrange(length): minIndex = i for j in xrange(i+1, length): if A[j] < A[minIndex]: minIndex = j A[minIndex], A[i] = A[i], A[minIndex] return A
# 直接插入排序 class Solution: # @param {int[]} A an integer array # @return nothing def sortIntegers(self, A): # Write your code here # 思想:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置, # 直到全部记录插入完成为止 length = len(A) for i in xrange(1, length): j = i - 1 while (j >= 0 and A[j] > A[j+1]): A[j], A[j+1] = A[j+1], A[j] j -= 1 return A
冒泡排序
通过交换使相邻的两个数变成小数在前大数在后,这样每次遍历后,最大的数就“沉”到最后面了。重复N次即可以使数组有序。
冒泡排序改进1:在某次遍历中如果没有数据交换,说明整个数组已经有序。因此通过设置标志位来记录此次遍历有无数据交换就可以判断是否要继续循环。
冒泡排序改进2:记录某次遍历时最后发生数据交换的位置,这个位置之后的数据显然已经有序了。因此通过记录最后发生数据交换的位置就可以确定下次循环的范围了。
冒泡排序毕竟是一种效率低下的排序方法,在数据规模很小时,可以采用。数据规模比较大时,最好用其它排序方法。
# 冒泡排序 class Solution: # @param {int[]} A an integer array # @return nothing def sortIntegers(self, A): # Write your code here length = len(A) for i in xrange(length - 1, 0, -1): for j in xrange(1,i+1): if A[j] < A[j-1]: A[j], A[j-1] = A[j-1], A[j] return A
归并排序
当一个数组左边有序,右边也有序,那合并这两个有序数组就完成了排序。如何让左右两边有序了?用递归!这样递归下去,合并上来就是归并排序。
# 归并排序 class Solution: # @param {int[]} A an integer array # @return nothing def sortIntegers2(self, A): # Write your code here length = len(A) self.A = A self.temp = [0]*length self.mergeSort(0, length - 1) return self.A # 如何让两序列有序,我们可以通过先递归的分解数列,当分出来的小组只有一个数据的时候认为是有序的 # 接着递归不断回调,完成归并排序 def mergeSort(self, first, last): if (first < last): mid = (first + last) / 2 self.mergeSort(first, mid) self.mergeSort(mid + 1, last) self.merge(first, mid, last) # 将二个有序数列合并。只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个 # 然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。 def merge(self, first, mid, last): i, j = first, mid + 1 m, n = mid, last k = 0 while(i <= m and j <= n): if self.A[i] <= self.A[j]: self.temp[k] = self.A[i] k += 1 i += 1 else: self.temp[k] = self.A[j] k += 1 j += 1 while(i <= m): self.temp[k] = self.A[i] k += 1 i += 1 while(j <= n): self.temp[k] = self.A[j] k += 1 j += 1 for i in xrange(k): self.A[i + first] = self.temp[i]
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。
快速排序
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
快速排序 class Solution: # @param {int[]} A an integer array # @return nothing def sortIntegers2(self, A): # Write your code here self.quickSort(A, 0,len(A)) def quickSort(self, array, start, end): if start < end: #边界条件 # 不断获取中值指针,不断左右递归直到边界条件 parindex = self.partition(array, start, end) self.quickSort(array, start, parindex) self.quickSort(array, parindex + 1, end) def partition(self, array, start, end): index = start # 中值指针 num = array[start] # 初始比较值为第一个元素 for i in xrange(start+1, end): if array[i] < num: # 如果小于则中值指针 +1 index += 1 if index != i: # 如果中值指针不等于循环指针,则代表其中间有比第一个元素大的值,所以要把这个比较小的值换过去 # 使得比第一个元素小的值始终是连续的 array[index], array[i] = array[i], array[index] # 当循环结束后,第二个元素到中值指针所在元素这一区间内所有的值都比第一个元素小 # 所以将第一个元素跟指针元素交换,完成排序 array[start], array[index] = array[index], array[start] return index
快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试常常出现快速排序的身影。
希尔排序
先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
希尔排序 class Solution: # @param {int[]} A an integer array # @return nothing def sortIntegers(self, A): # Write your code here # 思想是将待排序数组根据步长序列(通常是/2)分成多个子序列,对各个子序列分别进行插入排序 n = len(A) gap = n / 2 while (gap > 0): for i in xrange(gap, n): while (i >= gap and A[i - gap] > A[i]): A[i], A[i-gap] = A[i-gap], A[i] # j -= gap 的目的是为了将步长为gap的序列进行插入排序 i -= gap gap /= 2 return A