数据结构-各类排序算法

在这里插入图片描述

1. 交换排序

冒泡排序

思想:将数据进行两两比较,共循环 n-1次;每次循环确定一个数的位置,全局有序,如:第一次循环将元素两两比较,确定最大或者最小值放在数组的最后一个位置length-1。第二次循环比较未确定位置的元素(n-1),得出无序中的最值放在数组的倒数第二个位置length-2…

  • 时间复杂度:

    • 最好时间复杂度:当数据有序时外循环只需一次就可以得出结果。
    • 最坏时间复杂度:当数据逆序时,外循环需要循环n-1次。
    • 平均时间复杂度:
  • 稳定性:稳定。

  • 代码:

    package 交换排序算法;
    
    import java.util.Arrays;
    //冒泡排序算法
    public class BubbleSort {
    
    	public static void main(String[] args) {
    		int[] arr = new int[]{1,2,3,4,5,7,6};
    		System.out.println(Arrays.toString(arr));
    		BubbleSort.bubbleSort(arr);
    		System.out.println(Arrays.toString(arr));
    	}
    	
    	/**
    	 * 冒泡排序
    	 * @param arr
    	 */
    	public static void bubbleSort(int[] arr) {
    		for(int i = 0;i < arr.length-1 ;i++) {
    			for(int j = 0 ;j<arr.length - 1 - i;j++) {
    				if(arr[j] > arr[j+1]) {
    					arr[j] = arr[j] + arr[j+1];
    					arr[j+1] = arr[j] - arr[j+1];
    					arr[j] = arr[j] - arr[j+1];
    				}
    			}
    		}
    	}
    	
    	public static void bubbleSort1(int[] arr) {
    		boolean flag = true;
    		int count = 0;
    		for(int i = 0 ;i<arr.length - 1;i++) {//比较length-1次就可以了
    			flag = true;
    			for(int j = 0; j < arr.length - 1 - i;j++) {//第一轮确定一个数,第二轮确定两个数.....
    				if(arr[j] > arr[j+1]) {
    					arr[j] = arr[j] + arr[j+1];
    					arr[j+1] = arr[j]-arr[j+1];
    					arr[j] = arr[j] - arr[j+1];
    					flag = false;
    				}
    			}
    			count++;
    			if(flag) {//说明这一趟没有交换,数组已经有序
    				break;
    			}
    			
    		}
    		System.out.println(count);
    	}
    }
    
快速排序

思想:
(1)确定数组中的第一个元素的位置:将第一个元素作为标准stard,
从数组的最后一个元素arr[j]和stard比较,如果值比它大,则j–,继续将从后往前和stard比较,直到找到比stard小的元素arr[j],将arr[j]替换arr[i];
然后从前往后和stard的比较,如果值比它小,则i++,否则退出循环,将arr[i]替换arr[j];当i=j时说明元素已经比较完,stard的位置已经确定,将stard替换arr[i]/arr[j]。
(2)递归计算stard左边的元素。
(3)递归计算stard右边的元素。

  • 时间复杂度:

    • 最好时间复杂度:
    • 最差时间复杂度:
    • 平均时间复杂度:
  • 稳定行:

  • 代码

    package 交换排序算法;
    
    import java.util.Arrays;
    
    public class QuickSort {
    
    	public static void main(String[] args) {
    		int[] arr = new int[] {4,1,3,5,2,8,6,7};
    		quickSort(arr, 0, arr.length-1);
    		System.out.println(Arrays.toString(arr));
    	}
    	
    	/**
    	 * 快速排序:一次循环只能确定一个数的位置
    	 * @param arr
    	 * @param start
    	 * @param end
    	 */
    	public static void quickSort(int[] arr,int start,int end) {
    		if(start < end) {//如果start = end 表示只有一个数,不需要排序
    		
    			//将数组的第一个元素作为标准数
    			int stard = arr[start];
    			//记录需要排序的下标
    			int low = start;
    			int high = end;
    			//循环找出比标准数大的数和比标准数小的数
    			while(low < high) {
    				//右边的数字和标准数比较,当右边的数字比标准数小时退出循环
    				while((arr[high] >= stard) && (low<high)) high--;
    				//让右边比标准数小的数替换左边的数
    				arr[low] = arr[high];
    				//左边的数字标准数比较,当左边的数字大于标准数时退出循环
    				while((arr[low] < stard) && (low<high)) low++;
    				//让左边比标准数大的数替换右边的数
    				arr[high] = arr[low];
    			}//当low = high时退出循环,此时这个数已经赋值到其它位置,需要被标准数替换
    			//已经找到标准数相对于数组的位置,比左边的数大,比右边的数小,但是左、右里面的大小位置不确定
    			arr[low] = stard;
    			
    			//处理左边的数字
    			quickSort(arr,start,low-1);
    			//处理右边的数字
    			quickSort(arr,low+1,end);
    		}
    	}
    }
    
    

2. 选择排序

简单选择排序
  • 时间复杂度:

    • 最好时间复杂度:
    • 最差时间复杂度:
    • 平均时间复杂度:
  • 稳定行:

  • 代码:

    package 选择排序;
    
    import java.util.Arrays;
    
    public class SelectSort {
    
    	public static void main(String[] args) {
    		int[] arr = new int[] { 4, 5, 2, 3, 1, 4, 6 };
    		selectSort1(arr);
    		System.out.println(Arrays.toString(arr));
    	}
    
    	/**
    	 * 选择排序算法1:只要找到比第一个小的都交换
    	 * @param arr
    	 */
    	public static void selectSort(int[] arr) {
    		for (int i = 0; i < arr.length - 1; i++) {
    			for (int j = i + 1; j < arr.length; j++) {
    				 if(arr[j] < arr[i]) {
    				 arr[j] = arr[j] + arr[i];
    				 arr[i] = arr[j] - arr[i];
    				 arr[j] = arr[j] - arr[i];
    				 }
    			}
    		}
    	}
    	
    	/**
    	 * 简单选择排序
    	 * 选择排序算法2:找到还未排序的元素中最小值的下标,如果下标和未排序的第一个下标
    	 *               不一致则交换
    	 * @param arr
    	 */
    	public static void selectSort1(int[] arr) {
    		for(int i = 0; i < arr.length-1;i++) {
    			int minIndex = i;
    			for(int j = i+1;j<arr.length;j++) {
    				if(arr[j] < arr[minIndex]) {
    					minIndex = j;
    				}
    			}
    			if(minIndex != i) {
    				arr[minIndex] = arr[minIndex] + arr[i];
    				arr[i] = arr[minIndex] - arr[i];
    				arr[minIndex] = arr[minIndex] - arr[i];
    			}
    		}
    	}
    }
    
堆排序

思想:
1.构造初始堆:将待排序的元素构造成一个大根堆

  • [1] 根据元素顺序构造一个完全二叉树。
  • [2] 从最后一个非终节点开始构造一个大跟堆。

2.堆排序:

  • [1] 将第一个节点和最后一个交换。
  • [2] 重新调整最后一个以外的树,调成为一个大跟堆(第一步)。
  • 时间复杂度:

    • 最好时间复杂度:
    • 最差时间复杂度:
    • 平均时间复杂度:
  • 稳定性:

  • 代码

   package 选择排序;

   import java.util.Arrays;

  //堆排序
  public class HeapSort {
	public static void main(String[] args) {
		int[] arr = new int[] {9,6,8,7,0,1,10,4,2};
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void heapSort(int[] arr) {
		//开始位置是最后一个非叶子节点,即最后一个节点的父节点
		int start = (arr.length-1)/2;
		//调整为大跟堆
		for(int i=start;i>=0;i--) {
			maxHeap(arr,arr.length,i);
		}
		//先把数组中的第0个和堆中的最后一个数交换位置,再把前面的处理为大跟堆
		for(int i = arr.length-1;i>0;i--) {
			int temp = arr[0];
			arr[0] = arr[i];
			arr[i] = temp;
			maxHeap(arr,i,0);
		}
	}
	public static void maxHeap(int[] arr,int size,int index) {
		//左子节点
		int leftNode = 2*index+1;
		//右子节点
		int rightNode = 2*index+2;
		int max = index;
		//和两个子节点分别对比,找出最大的节点
		if(leftNode<size && arr[leftNode] > arr[max]) {
			max = leftNode;
		}
		if(rightNode<size && arr[rightNode] > arr[max]) {
			max = rightNode;
		}
		//交换
		if(max != index) {
			int temp = arr[max];
			arr[max] = arr[index];
			arr[index] = temp;
			//交换位置以后,可能会破坏之前的大根堆,所以,之前排好的堆需要重新调   整
			maxHeap(arr,size,max);
		}
	}
   }
package _52.数据流中的中位数;

import java.util.Arrays;

/**
 * 堆排序:大根堆
 * @author Administrator
 *
 */
public class HeapSort {
	
	public static void main(String[] args) {
		int[] arr = {1,4,3,5,2,6};
		maxHeapSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	/**
	 * 堆排序的入口
	 * @param arr
	 */
	public static void maxHeapSort(int arr[]){
		//1.从堆的最后一个非叶子结点开始构造一个大根堆
		int start = (arr.length-1-1) / 2;//最后一个非叶子结点,下标从0开始的
		for(int i = start; i >= 0; i--){
			maxShift(i,arr.length - 1,arr);
		}
		System.out.println(Arrays.toString(arr));
		//2.将大根堆中的第一个元素和最后一个元素交换,交换后的最后一个元素就确定了自己的位置
		for(int i = arr.length - 1; i > 0; i--){
			arr[0] = arr[0] + arr[i];
			arr[i] = arr[0] - arr[i];
			arr[0] = arr[0] - arr[i];
			
			maxShift(0,i-1,arr);
		}
	}

	/**
	 * 调整大根堆
	 * @param low	需要调整的堆的根结点
	 * @param high	堆的最后一个结点
	 */
	public static void maxShift(int low,int high,int arr[]){
		//需要调整的堆的根结点
		int root = low;
		//左孩子结点
		int left = 2*low+1;
		while(left <= high){
			//找出左孩子结点和右孩子结点中值最大的一个
			if(left < high && arr[left] < arr[left+1])
				left++;
			//如果孩子结点的最大值比根结点大则交换
			if(arr[left] > arr[root]){
				arr[root] = arr[root] + arr[left];
				arr[left] = arr[root] - arr[left];
				arr[root] = arr[root] - arr[left];
				//交换后可能会破坏left的大根堆,继续调整
				root = left;
				left = 2 * root +1;
			}
			else{
				break;
			}
		}
	}
}

3. 插入排序

思想:将元素分为两个部分:有序部分和无序部分。将无序部分的第一个元素和有序部分的最后一个元素开始比较大小,如果无序的元素比有序的元素大,则将该无序的元素插入到该有序元素的后一个数。

直接插入排序
  • 时间复杂度:

    • 最好时间复杂度:
    • 最坏时间复杂度:
    • 平均时间复杂度:
  • 稳定性:

  • 代码:

    package 插入排序;
    
    import java.util.Arrays;
    
    public interface InsertSort {
    
    	public static void main(String[] args) {
    		int[] arr = new int[] {2,5,3,1,4,7,6};
    		insertSort(arr);
    		System.out.println(Arrays.toString(arr));
    	}
    	
    	/**
    	 * 直接插入排序
    	 * @param arr
    	 */
    	public static void insertSort(int[] arr) {
    		//对于整个数组
    		for(int i = 1 ;i<arr.length;i++) {//需要循环的次数&&对于arr[i]是需要插入的元素
    			//将数组分成两部分,arr[i-1]之前有序区,arr[i]之后无序区
    			
    			//当无序区的第一个数arr[i]小于有序区的最后一个数arr[i-1]时,需要将arr[i]插入到有序区中
    			if(arr[i] < arr[i-1]) {
    				//把当前需要插入的数字保存起来,以便有序区中比它大的数字向后挪
    				int tmp = arr[i];
    				
    				int j;
    				//从有序中确定tmp的位置,比tmp大的数字往后面挪,然后将其插入
    				for(j = i-1;j>=0 && tmp < arr[j];j--) {
    					arr[j+1] = arr[j];//将该数字往后挪,将该数字的值赋给后一个数字
    				}//当j=-1(将tmp插入到有序数组的第0个位置)、tmp>arr[j](将tmp插入到arr[j]的后面)结束循环
    				arr[j+1] = tmp;
    			}
    		}
    	}
    }
    
希尔排序
  • 时间复杂度:

    • 最好时间复杂度:
    • 最坏时间复杂度:
    • 平均时间复杂度:
  • 稳定性:

  • 代码:

    package 插入排序;
    
    import java.util.Arrays;
    
    public class ShellSort {
    	public static void main(String[] args) {
    		int[] arr = new int[] {2,5,7,3,8,1,9,0};
    		shellSort(arr);
    		System.out.println(Arrays.toString(arr));
    	}
    
    	/**
    	 * 希尔排序:将数组分成 d=n/2 个组,在每个组中采用直接插入排序
    	 * 
    	 * @param arr
    	 */
    	public static void shellSort(int[] arr) {
    
    		int d = arr.length/2;
    		while(d>0) {
    			for (int i = d; i < arr.length; i++) {//对于arr[d]是需要插入有序列d的元素
    				//对于一组中:arr[i-d]之前有序,arr[i]之后无序,需要将arr[i]这个元素插入到有序列中
    				if (arr[i] < arr[i-d]) {
    					int tmp = arr[i];
    					int j;
    					for (j = i-d; (j >= 0) && (tmp < arr[j]);) {
    						arr[j+d] = arr[j];
    						j = j-d;
    					} // 循环结束后arr[j]<tmp
    					arr[j+d] = tmp;
    				}
    			}
    			d = d/2;
    		}
    	}
    }
    
折半插入排序:
  • 时间复杂度:

    • 最好时间复杂度:
    • 最差时间复杂度:
    • 平均时间复杂度:
  • 稳定性:

  • 代码:

    
    

4. 归并排序

归并思想:将两个有序的数组合并到一个数组

二路归并排序

思想(递归):将数组左边的元素排好序,将数组右边的元素排好序,将两个有序的数组合并到一个数组。

  • 时间复杂度:

    • 最好时间复杂度:
    • 最坏时间复杂度
    • 平均时间复杂度:
  • 稳定性:

  • 代码:

    package 归并排序;
    
    import java.util.Arrays;
    
    public class MergeSort {
    	public static void main(String[] args) {
    		int[] arr = new int[] {1,5,3};
    		mergeSort(arr,0,arr.length-1);
    		System.out.println(Arrays.toString(arr));
    	}
    	
    	/**
    	 * 归并算法(递归)
    	 * @param arr
    	 * @param low
    	 * @param high
    	 */
    	public static void mergeSort(int[] arr,int low,int high) {
    		int mid = 0;
    		if(low<high) {
    			mid = (low+high)/2;	
    			mergeSort(arr,low,mid);
    			mergeSort(arr,mid+1,high);
    			merge(arr,low,mid,high);
    		}
    	}
    	
    	/**
    	 * 归并:将两个有序的数组合成一个新的有序数组
    	 * @param arr
    	 * @param low
    	 * @param mid
    	 * @param high
    	 */
    	public static void merge(int[] arr,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(arr[i] < arr[j])
    			{
    				temp[index++] = arr[i];
    				i++;
    			}
    			else {
    				temp[index++] = arr[j];
    				j++;
    			}
    		}
    		
    		//将较长数组的其它元素加入到temp的后面
    		while(i <= mid) {
    			temp[index++] = arr[i];
    			i++;
    		}
    		while(j <= high) {
    			temp[index++] = arr[j];
    			j++;
    		}
    		
    		//把临时数组中的数据放入原数组中
    		for(int k = 0;k<temp.length;k++) {
    			arr[k+low] = temp[k];
    		}
    	}
    }
    
    

5. 基数排序

思想:首先找出元素中值最大的元素,获取最大元素的位数来作为循环比较的次数。第一次循环:将元素安个位排序;第二次循环:按十位排序;第三次循环:按百位排序…

  • 时间复杂度:

    • 最好时间复杂度:
    • 最坏时间复杂度:
    • 平均时间复杂度:
  • 稳定性:

  • 代码:

    • 方法一:

      package 基数排序;
      
      import java.util.Arrays;
      
      //用一个二维数组来存比较后的元素,在用一个数组存放二维数组中每个数组的元素个数
      public class RedixSort {
      
      	public static void main(String[] args) {
      		int[] arr = new int[] {23,6,189,45,9,287,34,56,1,798,652,5};
      		radixSort(arr);
      		System.out.println(Arrays.toString(arr));
      	}
      	
      	public static void radixSort(int[] arr) {
      		//1.先找出数组中的最大值
      		int max = Integer.MIN_VALUE;
      		for(int i = 0;i < arr.length;i++) {
      			if(arr[i] > max) {
      				max = arr[i];
      			}
      		}
      		
      	    //2.计算最大值的位数
      		int maxLength = String.valueOf(max).length();
      		
      		//3.根据最大值的位数决定要循环的次数:n为除数
      		
      		//用于临时存储按个位、十位....比较好的元素
      		int[][] temp = new int[10][arr.length];
      		//用于记录temp数组中每个一位数组中的数组中的元素个数
      		int[] counts = new int[10];
      		
      		for(int i = 0,n=1;i < maxLength;i++,n*=10) {//第一次循环比较个位,第二次循环比较十位,第三次循环比较百位
      			
      			//将数组中的元素取余后存入桶中
      			for(int j = 0 ;j < arr.length;j++) {
      				//计算余数
      				int ys = arr[j]/n%10;
      				temp[ys][counts[ys]] = arr[j];
      				//记录数量
      				counts[ys]++;
      			}
      			
      			//将桶中的元素一次取出
      			int index = 0;
      			for(int k = 0;k<counts.length;k++) {
      				if(counts[k] != 0) {//判断该桶中的是否有元素
      					//遍历取出元素
      					for(int m = 0;m<counts[k];m++) {
      						arr[index++] = temp[k][m];
      					}
      					//将counts中的k位置的值为0,以便下一轮存放
      					counts[k] = 0;
      				}
      			}
      		}
      	}
      }
      
    • 方法二:

      package 基数排序;
      
      //根据桶中"先进先出"的性质:直接用一个队列数组来存放比较后的值
      public class RedixSortQueue {
      
      	public static void radixSort(int[] arr) {
      		// 1.先找出数组中的最大值
      		int max = Integer.MIN_VALUE;
      		for (int i = 0; i < arr.length; i++) {
      			if (arr[i] > max) {
      				max = arr[i];
      			}
      		}
      
      		// 2.计算最大值的位数
      		int maxLength = String.valueOf(max).length();
      
      		// 3.根据最大值的位数决定要循环的次数:n为除数
      
      		// 用于临时存储按个位、十位....比较好的元素
      		MyLinkQueue[] temp = new MyLinkQueue[10];
      
      		for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {// 第一次循环比较个位,第二次循环比较十位,第三次循环比较百位
      			// 将数组中的元素取余后存入桶中
      			for (int j = 0; j < arr.length; j++) {
      				// 计算余数
      				int ys = arr[j] / n % 10;
      				temp[ys].enQueue(arr[j]);
      			}
      
      			// 将桶中的元素一次取出
      			int index = 0;
      			for (int k = 0; k < temp.length; k++) {
      				arr[index++] = temp[k].deQueue();
      			}
      		}
      	}
      }
      
      package 基数排序;
      
      //队列的链式存储结构
      public class MyLinkQueue {
      
      	class DataNode {
      		int data;
      		DataNode next;
      	}
      
      	private DataNode front;// 队首节点
      	private DataNode rear;// 队尾节点,一直指向最后一个元素
      
      	public MyLinkQueue() {
      		front = new DataNode();
      		front.next = null;
      		rear = new DataNode();
      		rear = null;
      	}
      
      	/**
      	 * 判断节点是否为空
      	 * @return
      	 */
      	public boolean isEmpty() {
      		return rear == null;
      	}
      
      	/**
      	 * 进队:采用尾插入法,后进队的元素在后面
      	 * @param element
      	 * @return
      	 */
      	public boolean enQueue(int element) {
      		DataNode data = new DataNode();
      		data.data = element;
      		if (isEmpty()) {// 如果队列为空,则首节点和尾节点都指向它
      			front.next = data;
      			rear = data;
      			return true;
      		} else {//否则,只将尾节点指向它
      			// 将新增节点插入到链表的最后面
      			rear.next = data;
      			// 然后将rear尾节点指向最后一个元素
      			rear = data;
      			return true;
      		}
      	}
      	
      	/**
      	 * 出队:出首节点的后节点,因为采用的是尾插入法
      	 * @return
      	 * @throws RuntimeException
      	 */
      	public int deQueue() throws RuntimeException{
      		if(isEmpty()) {
      			throw new RuntimeException("队列为空");
      		}
      		if(front.next == rear) {//如果队列中只要一个节点时
      			int result = front.next.data;
      			front.next = null;
      			rear = null;
      			return result;
      		}
      		else {
      			int result = front.next.data;
      			front.next = front.next.next;
      			return result;
      		}
      	}
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值