JAVA数据结构之排序

本文深入讲解了各种排序算法,包括冒泡排序、快速排序、选择排序、插入排序、希尔排序、归并排序、堆排序、桶式排序和基数排序。详细分析了每种算法的实现原理、步骤和代码示例,同时提供了算法的时间复杂度和空间复杂度对比,帮助读者全面理解排序算法。
摘要由CSDN通过智能技术生成

交换排序

一、冒泡排序

  • 【排序思想】

相邻的元素进行比较,如有需要则进行交换,每完成一次循环就将最大元素排在最后面,依次进行。

                int[] a = new int[] { 2, 4, 1, 0, 9, 8, 7, 6, 5, 3 };
		/**
		 *一次循环:相邻两个元素比较,大的交换至后面的元素
		 *第一次循环将a.length个中最大的元素放置于a[a.length-1]的位置
		 *第二次循环将前a.length-1个元素中最大的元素放置于a[a.length-2]的位置
		 *【总结】:内层循环中的每次循环在a.length-1-i处停止即可;
		 */
		for (int i = 0; i < a.length - 1; ++i) {
			for (int j = 0; j < a.length - 1 - i; ++j) {
				if (a[j] > a[j + 1]) {

                                        /**无第三变量介入实现值交换
                                        a[j]=a[j]+a[j+1];
					a[j+1]=a[j]-a[j+1];
					a[j]=a[j]-a[j+1];
                                        */
					int temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
				}
			}
		}
		//遍历
		for (int i = 0; i < a.length; ++i) {
			System.out.print(a[i]+"\t");
		}
//输出结果
0	1	2	3	4	5	6	7	8	9	

二、快速排序

  • 【排序思想】选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的), 比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。
  • 【一次循环】:从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有继续比较下一个,直到找到第一个比基准值小的值才交换。找到这个值之后,又从前往后开始比 较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的 值才交换。直到从前往后的比较索引>从后往前比较的索引,结束第一次循环,此时,对于基准值 来说,左右两边就是有序的了。
//快速排序

int[] a = new int[] { 2, 4, 1, 0, 9, 8, 7, 6, 5, 3 };

public static void quickSort(int[] a, int low, int high) {
		int start = low;
		int end = high;
		int key = a[low];

		while (end > start) {
			// 从后往前比较:保证key右侧的值均大于key
			while ((end > start) && a[end] >= key)
				// 若没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后从前往后比较
				--end;	
			if (a[end] <= key) {
				int temp = a[end];
				a[end] = a[start];
				a[start] = temp;
			}
			// 从前往后比较:保证key左侧的值均小于key
			while ((end > start) && (a[start] <= key))
				// 若没有比关键值大的,比较下一个,直到有比关键值大的交换位置
				++start;
			if (a[start] >= key) {
				int temp = a[start];
				a[start] = a[end];
				a[end] = temp;
			}
			// 此时第一次循环比较结束,关键值的位置已经确定,左侧的值均小于关键值,右侧的值均大于关键值,但是两侧的顺序暂时不一定排序
		}
		// 递归:处理当前关键值左侧
		if (start > low) {
			// 左侧子序列,第一个索引位置为关键值索引-1
			quickSort(a, low, start - 1);
		}
		// 递归:处理当前关键值右侧
		if (end < high) {
			// 右侧子序列,关键值索引+1到最后一个
			quickSort(a, end + 1, high);
		}
	}

//执行结果:
0 1 2 3 4 5 6 7 8 9 

快速排序过程图例:

记录下标

void testQuickSort(int array[], int left, int right)
{
    int index = 0, key, temp;
    if (left < right)
    {
        key = array[left];
        index = left;
        for (int i = left + 1; i <= right; i++)
            if (array[i] < key)
            { /* 将所有小于KEY值的数组元素全都左移至比KEY值大元素的右边 */
                index++;
                temp = array[i];
                array[i] = array[index];
                array[index] = array[i];
            } /* 移动完之后,KEY值之后一个元素至array[index]均小于KEY值 */
        /* 将KEY值与array[index]交换值 */
        temp = array[left];
        array[left] = array[index];
        array[index] = temp;
        /* 交换之后,left<KEY<right */
    }
}

选择排序

一、直接选择排序

  • 【排序思想】

将当前序列最小的关键字放置在当前序列的最前面的位置

/**

【直接选择排序】的排序过程:
~在一组元素中a[i]到a[n]中选择具有最小关键码的元素
~若它不是这组元素中的第一元素,则将它与这组元素中的第一个元素对调
~除去具有最小关键字的元素,在剩下的元素中重复第①、②步,直到剩余元素只有一个为止

*/

/**
* 直接选择排序
* 
*/
int[] a = new int[] { 2, 4, 1, 0, 9, 8, 7, 6, 5, 3 };

for(int i=0;i<a.length-1;++i) {
    for(int j=i;j<a.length;++j) {
        if(a[i]>a[j]) {
	    a[i]=a[i]+a[j];
	    a[j]=a[i]-a[j];
	    a[i]=a[i]-a[j];
        }
    }
}
/**
*result:
*0	1	2	3	4	5	6	7	8	9
*/


//**********************************************************************
/**
*改进之后:减少交换的次数
*/

for(int i=0;i<a.length-1;++i) {
    int temp_index=i;//
    for(int j=i;j<a.length;++j) {
        if(a[temp_index]>a[j]) {
            temp_index=j;
        }
    }
    if(temp_ondex!=i) {
        a[i]=a[i]+a[temp_index];
        a[temp_index]=a[i]-a[temp_index];
        a[i]=a[i]-a[temp_index];
    }
}

/**
*result:
*0	1	2	3	4	5	6	7	8	9
*/

二、堆排序


    /**
     * @param array
     */
    public static void HeapSort(int[] array) {
        int i;
        int length = array.length;
        for (i = length / 2 - 1; i >= 0; i--) {
            adjustment(array, i, length);
        }
        for (i = length - 1; i >= 0; i--) {
            int temp = array[0];
            array[0] = array[i];
            array[i] = temp;
            adjustment(array, 0, i);
        }
    }

    /**
     * @param array
     * @param pos
     * @param length
     */
    public static void adjustment(int[] array, int pos, int length) {
        int child = 2 * pos + 1;
        if (child + 1 < length && array[child] > array[child + 1]) {
            child++;
        }
        if (child < length && array[pos] > array[child]) {
            int temp = array[pos];
            array[pos] = array[child];
            array[child] = temp;
            adjustment(array, child, length);
        }
    }

// test code
 int[] array = {12, 98, 32, 54, 31, 543, 7, 64, 52, 134, 309};
        System.out.println(Arrays.toString(array));
        /* Result: [12, 98, 32, 54, 31, 543, 7, 64, 52, 134, 309] */
        HeapSort(array);
        System.out.println(Arrays.toString(array));
        /* Result: [543, 309, 134, 98, 64, 54, 52, 32, 31, 12, 7] */

插入排序

一、直接插入排序 

  • 【排序思想】将一个序列看作两部分,初始时,将a[0]看作一个有序子序列,a[1]-a[length-1]看作无序子序列;自a[1]起依次插入至有序子序列中,直至最后一个元素不再小于前面子序列的任何一个元素时,排序结束。

for (int i = 1; i < a.length; ++i) {

	// 准备插入有序子序列的值--临时存储可能要替换的值
	int insertdata = a[i];

	// 被插入的位置(准备和前一个数值比较)
	int index = i - 1;

	// 若插入的数值比被插入的数值小
	while (index >= 0 && insertdata < a[index]) {
		// 将a[index]后移
		a[index + 1] = a[index];
		// 让index向前移动
		--index;
	}

	// 把插入的数值放入合适的位置
	a[index + 1] = insertdata;
}

二、折半插入排序

  • 【排序思想】默认a[low]~a[high]为有序子序列(low=0,high=i-1),a[i]与middle=(low+high)/2
    比较,middle大于a[i],则high=middle-1,进而比较a[low]~a[middle-1]的中间值;反之,middle小于a[i],则low=middle+1,进而比较a[middle+1]~a[high],逐渐找到合适的插入位置。
  • 避免了依次查找
int insertdata;//要插入的元素
		int middle;//折半的位置
		//插入的位置,a[low...high]为有序序列
		int low;//低索引值
		int high;//高索引值
		for (int i = 1; i < a.length; ++i) {
			// 插入的位置
			low = 0;
			high = i - 1;
			//找到合适的插入位置
			while (low <= high) {
				//折半
				middle = (low + high) / 2;
				if (a[middle] > a[i])
					high = middle - 1;
				else
					low = middle + 1;
			}
			insertdata = a[i];
			//移动元素位置,将insertdata插入到合适位置
			for (int j = i; j > high + 1; --j)
				a[j] = a[j - 1];
			a[high + 1] = insertdata;
		}

三、Shell排序

  • 【排序思想】先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
【特殊的插入排序】

1. 选择一个增量序列distance_1,distance_2,…,distance_k,其中distance_i>distance_j,distance_k=1;
2. 按增量序列个数 k,对序列进行 k 趟排序;
3. 每趟排序,根据对应的增量 distance_i,将待排序列分割成若干长度为 m 的子序列,分别对各子表进
行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长
度。

/**
	 * @param a  待排序数组
	 * @param distance  shell增量
	 */
	public void ShellInsertSort(int[] a,int distance) {
		for(int i=distance;i<a.length;++i) {
			if(a[i]<a[i-distance]) {
				int index;
				//临时变量:保存待插入元素
				int insertdata=a[i];
				a[i]=a[i-distance];
				//逐个后移找到要插入的位置
				for(index=i-distance;index>=0&&insertdata<a[index];index=index-distance) {
					a[index+distance]=a[index];
				}
				//插入
				a[index+distance]=insertdata;
			}
		}
	}

//测试代码==================================================================
	        //设置增量
		int distance = a.length/2;
                
		while(distance>=1) {
                        //希尔排序
			ShellInsertSort(a, distance);
                        //逐渐减小增量
			distance/=2;
		}
		
		

归并排序

/**
     * @param array
     * @param low
     * @param high
     * @return array be sorted
     */
    public static int[] sort(int[] array, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            sort(array, low, mid);
            sort(array, mid + 1, high);
            merge(array, low, mid, high);
        }
        return array;
    }

    /**
     * @param array 数组
     * @param low
     * @param mid
     * @param high
     * @name merge
     */
    public static void merge(int[] array, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];//创建临时变量
        int i = low;
        int j = mid + 1;
        int k = 0;
        while (i <= mid && j <= high) {
            if (array[i] < array[j]) {
                temp[k++] = array[i++];
            } else {
                temp[k++] = array[j++];
            }
        }
        while (i <= mid) {
            temp[k++] = array[i++];
        }
        while (j <= high) {
            temp[k++] = array[j++];
        }
        if (temp.length >= 0)
            System.arraycopy(temp, 0, array, low, temp.length);
       /* for (int x = 0; x < temp.length; x++) {
            array[x + low] = temp[x];
        }*/
    }

// test code
int[] array = {12, 98, 32, 54, 31, 543, 7, 64, 52, 134, 309};
System.out.println(Arrays.toString(array));
/* result: [12, 98, 32, 54, 31, 543, 7, 64, 52, 134, 309]*/
System.out.println(Arrays.toString(sort(array, 0, array.length - 1)));
/* result: [7, 12, 31, 32, 52, 54, 64, 98, 134, 309, 543]*/

桶式排序

基数排序

/**
     * @param array
     */
    public static void basicSort(int[] array) {
        int max = 0;
        for (int value : array) {
            if (value > max) {
                max = value;
            }
        }
        int times = 0;
        while (max > 0) {
            max /= 10;
            times++;
        }
        List<ArrayList> queen = new ArrayList<ArrayList>();
        for (int i = 0; i < 10; i++) {
            ArrayList<Object> q = new ArrayList<>();
            queen.add(q);
        }
        for (int i = 0; i < times; i++) {
            for (int value : array) {
                int x = value % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
                ArrayList tempq = queen.get(x);
                tempq.add(value);
            }
            int count = 0;
            for (int z = 0; z < 10; z++) {
                while (queen.get(z).size() > 0) {
                    ArrayList<Integer> c = queen.get(z);
                    array[count] = c.get(0);
                    c.remove(0);
                    count++;
                }
            }
        }
    }

排序算法复杂度总结

排序方法时间复杂度(最好)时间复杂度(平均)时间复杂度(最坏)空间复杂度稳定性
冒泡排序O(n^{2})O(n^{2})O(n)O(1)稳定
选择排序O(n^{2})O(n^{2})O(n^{2})O(1)不稳定
插入排序O(n^{2})O(n^{2})O(n)O(1)稳定
希尔排序O(n^{1.3})O(n^{2})O(n)O(1)不稳定
快速排序O(nlog_{2}n)O(n^{2})O(nlog_{2}n)O(nlog_{2}n)不稳定
归并排序O(nlog_{2}n)O(nlog_{2}n)O(nlog_{2}n)O(n)稳定
堆排序O(nlog_{2}n)O(nlog_{2}n)O(nlog_{2}n)O(1)不稳定
基数排序O(n+k)O(n+k)O(n+k)O(n+k)稳定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值