排序算法回顾

动画结合更利于学习.

冒泡排序(BubbleSort)

将最大(最小)的数字逐个的向外“冒”出,时间复杂度O(n2)。
内层循环: 相邻两个数字比较,满足条件则交换,此处的j<length-i-1表示已经冒泡出去的数组即为有序,不需要再次遍历。
外层循环: 当需要冒泡的数字只剩下一个时停止

public static void bubbleSort(int[] arra){
		for (int i = 0; i < arra.length-1; i++) {
			//相邻两两交换将最大的数字往后冒泡
			for (int j = 0; j < arra.length-i-1; j++) {
				if(arra[j] > arra[j+1]){
					int temp = arra[j];
					arra[j] = arra[j+1];
					arra[j+1] = temp;
				}
			}
		}
	}

直接插入排序(InsertSort)

时间复杂度O(n2)
外层遍历,若当前数字比前一个数字小,记录当前数字遍历此数字前的数字,直到比前一个数字大,然后将该数字插入到该数字的后面

public static void insertSort(int[] arra){
		//遍历所有的数字
		for (int i = 1; i < arra.length; i++) {
			//如果当前数字比前一个数字小
			if(arra[i] < arra[i-1]){
				//存储当前数字
				int temp = arra[i];
				int j;
				//遍历此数字之前的所有数字,直到该数字temp不小于前一个数字
				for (j = i-1; j >= 0 && temp < arra[j]; j--) {
					arra[j+1] = arra[j];
				}
				arra[j+1] = temp;
			}
		}
	}

希尔排序(ShellSort)

//有设定步长,解决较小的数字排在最后的情况
public static void shellSort(int[] arra){
		//步长逐渐除以2
		for (int step = arra.length/2; step>0; step/=2) {
			//遍历从arra[step]开始,
			for (int i = step; i < arra.length; i++) {
				//与step步之前的元素相比较
				for (int j = i - step; j >= 0; j -= step) {
					if(arra[j] > arra[j+step]){
						int temp = arra[j];
						arra[j] = arra[j+step];
						arra[j+step] = temp;
					}
				}
				System.out.println(Arrays.toString(arra));
			}
		}
	}

归并排序(MergeSort)

时间复杂度O(nlgn),不容易理解
利用分治思想,当时严老师C语言版数据结构中学过顺序表的合并,merge方法中的基本语句也就是体现了这样一种思想,学习这一算法的时候可以由简到繁。

例如:

	public static void main(String[] args) {
		int[] arra = new int[]{1,3,5,2,4,6};
		int[] temp = new int[arra.length];
		//
		int low = 0, high = arra.length - 1;
		//中间位置
		int mid = (low + high) / 2;
		//双指针
		int i = 0, j = mid + 1;
		
		System.out.println(i+" "+j);
		//临时下标
		int  index = 0;
		//归并
		while(i <= mid && j <= high){
			//合并顺序数组
			if(arra[i] < arra[j]){
				temp[index++] = arra[i++];
			}else{
				temp[index++] = arra[j++];
			}
		}
		//前半个数组有剩余
		while(i <= mid){
			temp[index++] = arra[i++];
		}
		//后半个数组有剩余
		while(j <= high){
			temp[index++] = arra[j++];
		}
		
		System.out.println(Arrays.toString(temp));
	}

基本语句了解后来了解分治,若给定无序数组{1,5,4,8,6,2,10}又将如何排序

//分治思想:一部分递归实例
1 5 4 8 6 2 10
|     |      |
此时low = 0, high = 6, mid = 3
--------开始分(向左递归,直到剩下一个数字,他本身就有序,自己可以拿纸画)----
{1,5,4,8} {6,2,10}
{1,5,4} {8}
{1,5} {4} 
{1}
我现在的理解就是分开治理,把一大部分分成几小部分,逐渐整体有序
//分
	public static void mergeSort(int[] arra, int low, int high){
		if(low<high){
			int mid = (low+high)/2;
			//处理左边
			mergeSort(arra, low, mid);
			//处理右边
			mergeSort(arra, mid+1, high);
			//归并
			merge(arra, low, mid, high);
		}
	}
	
	public static void merge(int[] arra, int low, int mid, int high){
		//临时数组存储分治好的有序元素
		int[] temp = new int[high-low+1];
		//记录第一个数组需要遍历的下标
		int i = low;
		//记录第二个数组需要遍历的下标
		int j = mid+1;
		//记录在临时数组中存放的下标
		int index = 0;
		//遍历两个数组取出最小的数字,放入临时数组中,有序数组的合并
		while(i<=mid && j<=high){
			if(arra[i]<arra[j]){
				temp[index] = arra[i];
				i++; index++;
			}else{
				temp[index] = arra[j];
				j++; index++;
			}
		}
		while(i<=mid){
			temp[index] = arra[i];
			i++; index++;
		}
		while(j<=high){
			temp[index] = arra[j];
			j++; index++;
		}
		//将排好序的temp存入arra
		for(int k=0;k<temp.length;k++){
			arra[low+k] = temp[k];
		}
	}

选择排序(SelectSort)

public static void selectSort(int[] arra){
		//遍历所有的数
		for (int i = 0; i < arra.length-1; i++) {
			int index = i;
			//把当前遍历的元素和后面的所有元素依次比较
			for (int j = i + 1; j < arra.length; j++) {
				//记录下最小的数字下标
				if(arra[j] < arra[i]){
					index = j;
				}
			}
			//内层循环完成后,如果此时的最小数字min发生变化,就将此时的min替换arra[i]
			if(index != i){
				int temp = arra[index];
				arra[index] = arra[i];
				arra[i] = temp;
			}
		}
	}

快速排序(QuickSort)

双指针加分治,平均时间复杂度O(nlgn)
【以数组最左边位置作为基准位pivot】

public static void quickSort(int[] arra, int left, int right){
		if(left<right){
			int i = left, j = right;
			//一趟排序
			//选定基准位
			int temp = arra[left];
			//当i>j时结束循环
			while(i < j){
				//j-- 直到arra[j]的值小于temp时停止,将arra[i] = arra[j]
				while(i<j && temp<=arra[j]) j--;
				arra[i] = arra[j]; 
				//i++ 直到arra[i]的值大于temp的时候停止,arra[j] = arra[i]
				while(i<j && temp>=arra[i]) i++;
				arra[j] = arra[i];
			}
			//arra[i] = temp
			arra[i] = temp;
			
			//多趟分治
			quickSort(arra, left, i-1);
			quickSort(arra, i+1, right);
		}
	}

【以[l,r]中的任意一个随机下标作为基准位】

	void quick_sort02(int[] a, int l, int r) {
	       if(l >= r) return;
	
	       int pos = new Random().nextInt(r - l + 1) + l; //这里+的是l
	       swap(a, pos, r);
	
	       // 基准位
	       int temp = a[r], i = l, j = l;
	       while(j < r) {
	           if(a[j] < temp) swap(a, i++, j);
	           j++;
	       }
	       // i最后指向的位置,就是第一个大于temp的位置
	       swap(a, i, r);
	
	       quick_sort02(a, l, i - 1);
	       quick_sort02(a, i + 1, r);
	   }
	
	   void swap(int[] a, int i, int j) {
	       int t = a[i]; a[i] = a[j]; a[j] = t;
	   }

基数排序

算法思想容易理解,代码较长
最外层循环次数,为数组中元素的最大位数
创建编号0到9十个桶,分别取数字的个位,十位,百位。。。入桶,再出桶

public static void radixSort(int[] arra){
		//存最大的数字
		int max = Integer.MIN_VALUE;
		for(int i=0;i<arra.length;i++){
			if(arra[i]>max){
				max = arra[i];
			}
		}
		//计算最大的数字位数
		int maxLength = (max+"").length();
		//用于临时村塾数据的数组
		int[][] temp = new int[10][arra.length];
		//标记数组,记录temp每列数组的长度
		int[] counts = new int[10];
		//根据最大长度的数决定比较的次数
		for(int i=0,n=1;i<maxLength;i++,n*=10){
			for(int j=0;j<arra.length;j++){
				//第i次放入桶中
				int ys = arra[j]/n%10;
				temp[ys][counts[ys]] = arra[j];
				counts[ys]++;
			}
			//出桶
			int index = 0;
			for(int k=0;k<counts.length;k++){
				//k为temp的行数,counts[k]为temp在该行的数据元素长度
				if(counts[k]!=0){//表示该桶内有数据
					for(int m=0;m<counts[k];m++){
						arra[index++] = temp[k][m];
					}
					//将counts[k]置为0。将桶清空,以便于进行下一步的计数
					counts[k] = 0;
				}
			}
		}
	}

堆排序

/**
	 * 堆排序,将大根堆根节点的数字与数组末尾的数字交换位置,
	 * 堆中元素长度-1
	 * 
	 * @param arra
	 */
	public static void heapSort(int[] arra){
		//开始位置是最后一个非叶子结点,即最后一个结点的父结点
		int start = (arra.length-1)/2;
		//建立大根堆
		for(int i=start;i>=0;i--){
			maxHeap(arra, arra.length, i);
		}
		//开始排序
		for(int i=arra.length-1;i>=0;i--){
			//将大根堆根节点的数字与数组末尾的数字交换位置,最大的元素放在数组末尾
			int temp = arra[i];
			arra[i] = arra[0];
			arra[0] = temp;
			//此时只有堆顶的元素(下标为0)不满足大根堆,需要调整
			maxHeap(arra, i, 0);
			//再次满足大根堆,堆中元素长度-1
		}
	}
	
	/**
	 * 建立大根堆
	 * [10, 9, 6, 5, 4, 1, 2]
	 * 父结点的权大于子节点的权
	 */
	public static void maxHeap(int[] arra, int size, int index){
		//存储当前结点,假设当前结点的值最大
		int max = index;
		//左子节点下标
		int leftNode = 2*index+1;
		//右子节点下标
		int rightNode = 2*index+2;
		//与两个子结点数值比较,找出数值最大的结点下标
		if(leftNode<size && arra[leftNode]>arra[max]){
			max = leftNode;
		}
		if(rightNode<size && arra[rightNode]>arra[max]){
			max = rightNode;
		}
		//如果max的值发生变化
		if(max != index){
			//交换索引下标对应的数字
			int temp = arra[index];
			arra[index] = arra[max];
			arra[max] = temp;
			//System.out.println(Arrays.toString(arra));
			//交换完成最大的数字为两个较小子节点的父节点,
			//交换后需要避免对arra[max]在原处时的子节点产生影响,需要递归处理
			maxHeap(arra, size, max);
		}
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值