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

几种排序小结 JAVA语言描述

2018-03-12 08:20 211 查看

插入排序

针对一个已经排好序的子线性表,不断的插入元素进去,直到所有的元素都是排好序的。
对于一个有序的线性表list[ ],元素a=list[i]插入会依次与线性表中的元素进行比较。如果list
>a,则将list
后移一位至list[n+1](此时list
=list[n+1]);如果list[k]<a ,此时list[k+1] = list[k+2]>a 则a插入list[k+1]。public static void insertionSort(int[] list) {
for (int i = 1; i < list.length; i++) {
int temp = list[i]; //从数组的第2个元素开始
int k;
for (k = i - 1; k >= 0; k--) {
if (temp < list[k]) {
list[k + 1] = list[k];//如果元素大于temp,
//则把当前的元素后移,直到第一个元素或者当前元素大于temp
} else
break;
}
list[k + 1] = temp;//把元素插入第k个元素位置上,此时有2种情况
//1、所有元素都大于temp,此时k=-1,k+1 =0。也就是说temp是最小的元素,此时应该在第一个
//2、下标为[k]的元素小于temp,此时list[k+1]=list[k+2]>temp,所以temp应该插入list[k+1]
}
}时间复杂度:
对于一个有着n个元素的线性表来说,第一次迭代需要比较2个元素的值并且还需要检测下标(1次比较1次移动=2×1),第二次迭代需要比较3个元素的值和下标(2次比较2次移动2×2)。设c为每次迭代的赋值等其他操作。那么时间的复杂度就是
T(n)=2×1+2×2+2×3+2×4+···+2×(n-1)+c×(n-1)



冒泡排序

冒泡排序会多次遍历数组,在每次遍历中连续比较相邻的元素,如果元素没有按照顺序排列,则互换它们的值。
由于较大的数字不断的与相邻的数字比较并且向数组的末尾移动,而较小的数字不断的向数组的头部移动,这看上去类似于“冒泡”,所以称之为“冒泡排序”。
下面是我自己写的冒泡排序的代码:public static void BubbleSort(int[] list) {
for (int i = 0, k = 1; (i < list.length - k)
&& (k <= list.length - 1); i++) {

int temp = list[i];
if (list[i] > list[i + 1]) {
list[i] = list[i + 1];
list[i + 1] = temp;
}
if (i == list.length - k - 1) {
k++;
i = -1;
} // 每次循环到末尾的时候,末尾已经是线性表的最大数,因此k+1,下次循环就不需要再比较这个末尾的数字
}
}下面是书籍中的代码:public static void BubbleSort1(int[] list) {
boolean needNextPass = true;
for (int k = 1; k < list.length && needNextPass; k++) {
needNextPass = false;
for (int i = 0; i < list.length - k; i++) {
if (list[i] > list[i + 1]) {
int temp = list[i];
list[i] = list[i + 1];
list[i + 1] = temp;
needNextPass = true;
}
}
}
}为了区别这2个代码的性能优劣,我随机了1w个整型的数据进行比较。使用BubbleSort的时间是140毫秒BubbleSort1的时间是107毫秒。使用10w个整型的结果是,使用BubbleSort是17835毫秒,而使用BubbleSort1是12027毫秒。分析可知:第二段代码中的needNextPass是提高性能的关键,在已经排好序的情况下(程序没有完全遍历数组就已经是排好序的情况)程序将不会再进行完整的遍历。因此节省了时间。
时间复杂度:



归并排序

归并排序将数组分为两半,对每部分递归的应用归并排序。在两部分都排好序后,对它们进行归并。
如果 list.length > 0,则会报错。原因是如果list.length = 1,那么会导致有个数组的初始长度被设置为0.public static void mergeSort(int[] list) {
if (list.length > 1) { //这里必须是1,如果是0的话会报java.lang.StackOverflowError错误
int[] firstHalf = new int[list.length / 2];
System.arraycopy(list, 0, firstHalf, 0, list.length / 2);
mergeSort(firstHalf);

int[] secondHalf = new int[list.length - list.length / 2];
System.arraycopy(list, list.length / 2, secondHalf, 0,
secondHalf.length);
mergeSort(secondHalf);

merge(firstHalf, secondHalf, list);
}
}

public static void merge(int[] list1, int[] list2, int[] temp) {
int current1 = 0; // 这3个int用于下标
int current2 = 0;
int current3 = 0;
while (current1 < list1.length && current2 < list2.length) {
if (list1[current1] < list2[current2]) {
//list1当前数小,则加入temp,并且temp和list1的下标增加
temp[current3++] = list1[current1++];
} else {//list2当前数小,则加入temp,并且temp和list2的下标增加
temp[current3++] = list2[current2++];
}
}//如果循环跳出后,list1或者list2还未完结,那么就把剩下的元素直接插入temp
while(current1<list1.length) { temp[current3++] = list1[current1++]; }
while(current2<list2.length) { temp[current3++] = list2[current2++]; }
}时间复杂度:



快速排序

在数组中选择一个称为主元(pivot)的元素,将数组分为两部分,使得第一部分中的所有元素都小于或等于主元,而第二部分中的所有元素都大于主元。对第一部分递归的用快速排序算法,然后对第二部分递归的应用快速排序算法。
首先是默认选择首个元素为主元(pivot)的排序代码:private static void quickSort(int[] list, int first, int last) {
if (last > first) {
int pivotIndex = partition(list, first, last);
quickSort(list, first, pivotIndex - 1);
quickSort(list, pivotIndex + 1, last);
}
}

/** Partition the array list[first..last] */
private static int partition(int[] list, int first, int last) {
int pivot = list[first]; // Choose the first element as the pivot
int low = first + 1; // Index for forward search
int high = last; // Index for backward search

while (high > low) {
// Search forward from left
while (low <= high && list[low] <= pivot)
low++;

// Search backward from right
while (low <= high && list[high] > pivot)
high--;

// Swap two elements in the list
if (high > low) {
int temp = list[high];
list[high] = list[low];
list[low] = temp;
}
}

while (high > first && list[high] >= pivot)
high--;

// Swap pivot with list[high]
if (pivot > list[high]) {
list[first] = list[high];
list[high] = pivot;
return high;
}
else {
return first;
}
}这种方法在倒序的数组中复杂度最高,因为每次都会分成一个包含list.length-1个元素的数组和一个0个元素的数组,剩下来就是这个主元。
时间复杂度:

平均情况下的时间复杂度为 T(n) = O(nlogn)
最差情况下的时间复杂度为 T(n) = O(n*n)
使用“三项数据取中”的办法处理:
通过选取特定的中间值的点来进行pivot的设置,从而避免倒序取首位的情况。public static void recQuickSort(int left, int right) {
if (right - left +
4000
1 <= 3) {
manualSort(left, right);
} else {
//找出首、尾和中点 3者中的中间值pivot,将pivot放入right-1位置
int median = medianof3(left, right);
//根据pivot找出划分list的下标的值partition
int partition = partitionIt(left, right, median);
//根据partition来继续将list划分成左右两块
recQuickSort(left, partition - 1);
recQuickSort(partition + 1, right);
}
}

private static int partitionIt(int left, int right, int median) {
// TODO Auto-generated method stub
int leftPtr = left;
int rightPtr = right - 1;
while (true) {
while (list[++leftPtr] < median);
while (list[--rightPtr] > median);

if (leftPtr < rightPtr)
swap(leftPtr, rightPtr);
else break;
}
swap(leftPtr,right-1);
return leftPtr;
}

private static int medianof3(int left, int right) {
// TODO Auto-generated method stub
int center = (left + right) / 2;
if (list[left] > list[center])
swap(left, center);
if (list[left] > list[right])
swap(left, right);
if (list[center] > list[right])
swap(center, right);

swap(center, right - 1);

return list[right - 1];
}

public static void manualSort(int left, int right) {
if(right-left+1 ==1) return;
else if(right-left+1==2) {
if(list[left]>list[right]) swap(left,right);
} else {
int center = (left + right) / 2;
if (list[left] > list[center])
swap(left, center);
if (list[left] > list[right])
swap(left, right);
if (list[center] > list[right])
swap(center, right);
}
}
public static void swap(int left, int right) {
int temp = list[left];
list[left] = list[right];
list[right] = temp;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: