您的位置:首页 > 编程语言 > Python开发

算法基础——十种常用排序算法的Java及Python实现

2016-04-01 14:58 821 查看

概述

排序算法不用多说了,程序员算法基础必须要掌握的,现在总结一下加深记忆。下图是常见八大排序算法的分类、名称、时间空间复杂度,以及稳定性。


除了以上八大排序算法外,还有两种在输入数组符合特定条件下的高效率排序算法:
计数排序:假设输入数组中每一个元素都是[0,k]区间内的整数。对于一般情况,k取数组中的最大值。这是一种稳定排序,当k=O(n)时,时间复杂度O(k+n)=O(n),但当k很大时,带来空间的代价是昂贵的。
桶排序:假设输入数组的中每一个元素均匀分布在[0,1)区间上。跟计数排序一样是一种稳定排序,因为对输入数据作了假设,速度较快。当不符合均匀分布时只要满足以下条件也能使得时间复杂度为O(n):所有桶的大小的平方和与总的元素数呈线性关系。

代码

以下是经典八大排序算法的Java及Python代码,都是基于经典算法书籍《算法导论》里的伪代码实现的,我在关键语句部分附上了注释。
按照上图中的顺序分别介绍八大排序算法的实现(升序),前面是Java,后面是Python。Java的排序函数写在了一个类里,Python的排序函数则直接写出来了。

直接插入排序

public class InsertSort {
//插入排序,最好情况O(n),最坏情况O(n^2),稳定原址排序
public int[] Sort(int[] arr){

for(int i=0;i<arr.length;i++){
int key = arr[i];
int j = i-1;
for(;j>=0 && arr[j]>key;j--)
arr[j+1] = arr[j];//注意!这里不是交换,而是把比key大的都往后挪
arr[j+1] = key;//key再插入合适的位置
}
return arr;
}
}

def insertSort(arr):
#插入排序,最坏为O(n^2),最好为O(n)
#升序排列一个数组,降序排列将while语句中改为arr[i]<key即可
if(arr==None or len(arr)<2):
return arr
for j in range(1,len(arr)):
key = arr[j]#待插入的数
i = j-1
while(i>=0 and arr[i]>key):
#从后往前比较待插入的数和当前数,将arr[j-1]、arr[j-2]...向右移动直到找到arr[j]的适当位置
arr[i+1] = arr[i]
i -= 1
arr[i+1] = key#遍历完将arr[j]插入到该位置
return arr

希尔排序

public class ShellSort {
// 希尔排序,最好情况O(n),最坏情况O(n^2),平均情况比直接插入排序要好,平均情况为O(n^1.3)
//不稳定,原址排序
public int[] Sort(int[] arr) {
int n = arr.length;
for (int gap = n; gap > 0; gap/=2) {

for (int i = gap;i < n;i++){
if (arr[i] < arr[i-gap]){//如果比前面的元素小,则以步长为gap进行插入排序
int key = arr[i];
int j = i-gap;
while (j>=0 && arr[j] > key){
arr[j+gap] = arr[j];
j -= gap;
}
arr[j+gap] = key;
}
}
}
return arr;
}
}
def shellSort(arr):
#改进版的插入排序-希尔排序,最坏为O(n^2),最好为O(n),平均为O(n^1.3)
gap = len(arr)/2
while gap > 0:

for i in range(gap,len(arr)):
if arr[i] < arr[i-gap]:#后面的元素比前面的小,以gap为步长进行插入排序
key = arr[i]
j = i - gap
while j>=0 and arr[j]>key:
arr[j+gap] = arr[j]
j -= gap
arr[j+gap] = key
#步长减一半
gap /= 2

return arr

直接选择排序

public class SelectSort {
//选择排序,最坏和最好都为O(n^2),不稳定,原址排序
public int[] Sort(int[] arr) {
int n = arr.length;
if (n<2)
return arr;

for(int i=0;i<n-1;i++){
int index = i;
for(int j=i+1;j<n;j++){
if(arr[j]<arr[index]){
index = j;//找出剩余元素中最小那个数的下标
}
}
//将剩余元素中最小那个数跟当前数交换
int tmp = arr[i];
arr[i] = arr[index];
arr[index] = tmp;
}

return arr;
}
}
def selectSort(arr):
#选择排序,最坏为O(n^2)
#升序排列一个数组,降序排列将while语句中改为arr[i]<key即可
if(arr==None or len(arr)<2):
return arr
for j in range(len(arr)):

index = j
for i in range(j,len(arr)):
if arr[i] < arr[index]:
index = i#找出剩余的最小元素

#未排序中最小的元素跟arr[j]交换
tmp = arr[j]
arr[j] = arr[index]
arr[index] = tmp

return arr

堆排序

public class HeapSort {
//维护一个最大堆,使其以i节点的元素为根
public void maxHeapify(int[] arr,int i,int heap_size){
int l = 2*i+1;//左孩子节点
int r = l+1;

int largest = i;
if(l<heap_size && arr[l]>arr[largest])
largest = l;
if(r<heap_size && arr[r]>arr[largest])
largest = r;

if(largest != i){
//保证i节点为最大,并维护以largest节点为根的最大堆
int tmp = arr[i];
arr[i] = arr[largest];
arr[largest] = tmp;
maxHeapify(arr,largest,heap_size);
}
}

public void buildMaxHeap(int[] arr){
int heap_size = arr.length;
int mid = (heap_size-1)/2;
//从中间节点开始把arr建为最大堆(只需遍历数组的一半),最后使得arr[0]为最大堆的根
for(int i=mid;i>=0;i--)
maxHeapify(arr,i,heap_size);
}

public int[] Sort(int[] arr){
//先把数组建造为最大堆
buildMaxHeap(arr);

//从后往前遍历,根据最大堆的性质arr[0]最大
for(int i = arr.length-1;i>=0;i--){
int tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
int heap_size = i;
maxHeapify(arr,0,heap_size);
}

return arr;
}
}
def maxHeapify(arr,i,heap_size):
#维护一个最大堆,让arr[i]的值在最大堆中逐级下降,使得以i为根节点的子树是最大堆,O(lgn)
l = 2*i+1#下标i从0开始,因此左孩子节点的下标是2*i+1
r = l + 1#右孩子节点

largest = i

if l<heap_size and arr[l]>arr[largest]:#注意heap_size初始化为len(arr),这里判断应为l<heap_size
largest = l
if r<heap_size and arr[r]>arr[largest]:
largest = r

if largest != i:#i不是最大堆的根节点,就交换值,并且让largest为根节点的子树保持最大堆
arr[largest],arr[i] = arr[i],arr[largest]
maxHeapify(arr,largest,heap_size)

def buildMaxHeap(arr):
#自顶向上,将arr转换为最大堆
heap_size = len(arr)
mid = int((heap_size-1)/2)
for i in range(mid,-1,-1):
maxHeapify(arr,i,heap_size)

def heapSort(arr):

buildMaxHeap(arr)#时间复杂度O(n)
size = len(arr)

#n-1次调用maxHeapify,时间复杂度O(nlgn)
for i in range(size-1,-1,-1):

arr[i],arr[0] = arr[0],arr[i]#从后往前存储,根据最大堆性质,arr[0]是当前最大堆的最大值
heap_size = i
maxHeapify(arr,0,heap_size)



冒泡排序

public class PopSort {
public int[] Sort(int[] arr) {
int n = arr.length;
if(n<2)
return arr;

for(int i=0;i<n;i++)
for(int j=n-1;j>i;j--){
if(arr[j-1]>arr[j]){
int tmp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = tmp;
}
}

return arr;
}
}
def popSort(arr):
n = len(arr)
if n < 2:
return arr

for i in range(n):
for j in range(n-1,i,-1):
if arr[j-1] > arr[j]:
arr[j-1],arr[j] = arr[j],arr[j-1]

return arr

快速排序

public class QuickSort {

public int getPartition(int[] arr,int low,int high){

int tmp;
int index = low - 1;
for(int i = low;i<high;i++){
//arr[index]及其以前的都比arr[high]小
if(arr[i]<=arr[high]){
index++;
tmp = arr[i];
arr[i] = arr[index];
arr[index] = tmp;
}
}
//把arr[high]放到合适位置
tmp = arr[index+1];
arr[index+1] = arr[high];
arr[high] = tmp;

return index+1;
}

public void Sort(int[] arr,int low,int high) {
//选一个基准元素将arr分成左边小右边大的两部分,注意high是最大下标
if(low<high){
int mid = getPartition(arr,low,high);
Sort(arr,low,mid-1);
Sort(arr,mid+1,high);
}
}
}
def quickSort(arr,low,high):
#随机选择基准元素的快速排序,达到期望时间复杂度O(nlgn)
#注意high是最大下标
if low < high:
mid = getPartition(arr,low,high)
#对基准元素两边的数组快排
quickSort(arr,low,mid-1)
quickSort(arr,mid+1,high)

def getPartition(arr,low,high):
#随机选择一个数并与arr[high]交换,防止最坏情况
#将arr[high]作为基准进行排序
rand = random.randint(low,high)
arr[high],arr[rand] = arr[rand],arr[high]

index = low - 1
for i in range(low,high):
if arr[i] <= arr[high]:#保证index前面的数都比key小
index += 1
arr[index],arr[i] = arr[i],arr[index]
arr[index+1],arr[high] = arr[high],arr[index+1]

return index+1

归并排序

public class MergeSort {

public void Sort(int[] arr,int low,int high){
if(low<high){
//注意high是最大下标
int mid = (int)((low+high)/2);
Sort(arr,low,mid);
Sort(arr,mid+1,high);
mergeArr(arr,low,mid,high);
}
}

public void mergeArr(int[] arr,int low,int mid,int high){
int n1 = mid-low+1;//注意左边数组的长度
int n2 = high-mid;
int[] arr1 = new int[n1+1];
int[] arr2 = new int[n2+1];

int i = 0;
int j = 0;
//把mid左右两边的数分别放在两个数组里
for(;i<n1;i++)
arr1[i] = arr[low+i];
for(;j<n2;j++)
arr2[j] = arr[mid+1+j];
//放置无穷大的值作为哨兵值,简化边界处理
arr1[n1] = Integer.MAX_VALUE;
arr2[n2] = Integer.MAX_VALUE;
i = 0;
j = 0;
for(int k=low;k<=high;k++){
//比较左右两个数组的元素,将小的依次放入arr[k]
if(arr1[i]<=arr2[j]){
arr[k] = arr1[i];
i++;
}
else{
arr[k] = arr2[j];
j++;
}
}
}
}
def mergeSort(arr,p,r):
#p17,归并排序,O(nlgn).注意r是数组的最大下标
if p<r:
q = int((p+r)/2)
mergeSort(arr,p,q)
mergeSort(arr,q+1,r)
Merge(arr,p,q,r)

def Merge(arr,p,q,r):
#合并两个有序数组,arr1[p...q]和arr2[q+1...r]
n1 = q-p+1#左边数组的长度(因为q中位数是向下取整,所以q-p+1)
n2 = r-q#右边数组的长度
arr1 = []
arr2 = []
for i in range(n1):
arr1.append(arr[p+i])
for j in range(n2):
#注意j的范围
arr2.append(arr[q+1+j])
arr1.append(float('inf'))#放置一个无穷大的数作为“哨兵值”
arr2.append(float('inf'))
#print(arr1,arr2)
i,j = 0,0
for k in range(p,r+1):#注意k的范围
if arr1[i] <= arr2[j]:
arr[k] = arr1[i]
i += 1
else:
arr[k] = arr2[j]
j += 1

基数排序

这个用Java写太麻烦了,各种麻烦的ArrayList操作~~所以只贴Python版
def cntDigit(arr,radix):  
    #获取数组元素的最大位数  
    maxnum = arr[0]  
    for x in arr:  
        if x > maxnum:  
            maxnum = x  
      
    cnt = 0  
    while(maxnum != 0):  
        maxnum //= radix  
        cnt += 1
    print("max radix="+str(cnt))  
    return cnt  
  
def radixSort(arr,radix=10):  
    k = cntDigit(arr,radix)#获取最大位数  
    bucket = [[] for i in range(radix)] #10个桶 
    for i in range(1,k+1):  
        for j in arr:  
            #bucket[x]存储从低到高第i位为x的数,如数组中的543,i=1时存在bucket[3]里  
            bucket[j//(radix**(i-1)) % (radix)].append(j)  
        del arr[:]#先初始化arr  
        #print(bucket)  
        for z in bucket:#当前位数的数组按顺序放入arr中  
            arr += z  
            del z[:]
        #print(arr)
    return arr
arr = [431,234,560,102,322,676,148,329,543,89,7]
sortedArr = radixSort(arr)
print(sortedArr)

计数排序

def countSort(a):
k = a[0]
#得到最大值k
for x in a:
if x > k:
k = x
b = [0 for i in range(len(a))]#b存放排序的输出
c = [0 for i in range(k+1)]#c是计数数组

for x in a:
#c[x]记录了数组a中x出现的次数
c[x] += 1
for i in range(1,k+1):
#[i]记录了小于等于i的元素的数量
c[i] = c[i] + c[i-1]
for x in a:
#小于等于x的元素有c[x]个,将x放在第c[x]的位置
#注意下标从0开始
b[c[x]-1] = x
c[x] -= 1
return b

桶排序

import random

def bucketSort(a):
n = len(a)
b = [[] for i in range(n)]#产生n个链表
for i in range(n):
#第i个链表存放的是半开区间[i/10,(i+1)/10]的值
buc = int(n*a[i])
b[buc].append(a[i])
res = []
for list in b:
insertSort(list)#插入排序
res += list
return res

def insertSort(arr):
#对每一个链表进行插入排序
if(arr==None or len(arr)<2):
return arr
for j in range(1,len(arr)):
key = arr[j]#待插入的数
i = j-1
while(i>=0 and arr[i]>key):
#从后往前比较待插入的数和当前数,将arr[j-1]、arr[j-2]...向右移动直到找到arr[j]的适当位置
arr[i+1] = arr[i]
i -= 1
arr[i+1] = key#遍历完将arr[j]插入到该位置
return arr

#测试用例:
#产生一个[0,1)区间均匀分布的数组
ranArr = [random.random() for i in range(10)]
print(ranArr)
res = bucketSort(ranArr)
print(res)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  排序算法 java python