《数据结构与算法分析》笔记---排序问题

本文深入讲解了七种经典排序算法,包括插入排序、希尔排序、堆排序、归并排序、快速排序、冒泡排序和选择排序。每种算法都附有详细的步骤说明和示例,帮助读者理解其工作原理及时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本书我先看了排序这一章节,因为在牛客网做题的时候,题目总是会涉及到排序问题,所以要复习排序问题。

1、插入排序

        插入排序是从后向前扫描,找到相应位置并插入,比如数组34,8,64,52,32,21.对其进行排序

         第一趟:8,34,64,52,32,21.因为8<34,就插在34前面

         第二趟:8,34,64,52,32,21.因为34<64,顺序不变

         第三趟:8,34,52,64,32,21.因为52<64,所以改变顺序,接着52又继续和34相比较,比34大,也比8大,所以就在它们前面

         第四趟:8,32,34,52,62,21.

          第五趟:8,21,32,34,52,62.

          排序结束,接下来就是算法实现

private static void Insert(int[] arr) {

		for(int i=0;i<arr.length;i++) {
			int temp=arr[i];
			for(int j=i;j>0 && temp<arr[j-1];j--) {
				arr[j]=arr[j-1];
				arr[j-1]=temp;
			}
		}
		for (int i : arr) {
			System.out.println(i);
		}
	}

2、希尔排序

    先将整个待排序的序列分几次进行分割成若干份子序列,后按照份数进行进行直接插入排序,直到相邻的数进行比较。

    举个栗子^-^:81,94,11,96,12,35,17,95,28,58,41,75,15进行排序

    先分成5份排序后:35,17,11,28,12,41,75,15,96,58,81,94,95

    接着分成3份排序后:28,12,11,35,15,41,58,17,94,75,81,96,95

    最后分成1份排序后:11,12,15,17,28,35,41,58,75,81,94,95,96

 

import java.util.Scanner;

/**
 * 希尔排序
 * 
 * @author thinkpad_ljj
 *
 */
public class Shellsort {

	public static void main(String[] args) {
		Scanner read = new Scanner(System.in);
		int n = read.nextInt();
		int[] arr = new int[n];
		for(int i = 0;i<n;i++) {
			arr[i]=read.nextInt();
		}
		
		shellSort(arr);
	}

	/**
	 * 从小到大排序
	 * 确定每次分成gap份子序列,gap每次缩短为原来的一半
	 * 当确定了步长后,就要按照i的顺序找到步长所对应的值
	 * 和间隔步长的数值进行比较,若小于就交换
	 * @param arr 传入数组序列
	 */
	private static void shellSort(int[] arr) {
		//先确定分成多少分子序列
		for(int gap = arr.length/2;gap>0;gap/=2) {
			for(int i=gap;i<arr.length;i++) {
				int temp = arr[i];
				for(int j=i;j>=gap&&arr[j-gap]>temp;j-=gap) {
					arr[j]=arr[j-gap];
					arr[j-gap]=temp;
				}
			}
		}
	}
}

3、堆排序

基本思想:这里讨论的是大根堆,小根堆同理,先对堆进行大根堆处理即每个的父节点都是较大的,然后开始排序,根节点和最后的叶子结点进行交换,接着又进行大根堆处理,如此循环直到排序结束。

package sort;

import java.util.Scanner;

/**
 * 堆排序
 * 
 * @author thinkpad_ljj
 *
 */
public class HeapSort {

	public static void main(String[] args) {
		Scanner read = new Scanner(System.in);
		int n = read.nextInt();
		int[] arr = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = read.nextInt();
		}
		heapSort(arr);
	}

	/**
	 * 堆排序
	 * 
	 * @param arr数组序列
	 */
	private static void heapSort(int[] arr) {
		/* 构建大顶堆 */
		for (int i = arr.length / 2 - 1; i >= 0; i--) {
			buildHeap(arr, i, arr.length);
		}

		/* 调整堆结构+交换堆顶元素和末尾元素 */
//		for(int j=arr.length-1;j>0;j--) {
//			swap(arr,0,j);
//			buildHeap(arr,0,j);
//		}
//		
		for (int i : arr) {
			System.out.println(i);
		}
	}

	/**
	 * 交换堆顶元素和末尾元素
	 * 
	 * @param arr
	 * @param i   堆顶位置
	 * @param j   末尾位置
	 */
	private static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

	/**
	 * 构建大顶堆 只要是大的就往上抛
	 * 
	 * @param arr
	 * @param i      从下往上数第一个非叶子节点(从右往左),i每次定位都是父节点的位置
	 * @param length
	 */
	private static void buildHeap(int[] arr, int i, int length) {
		int child;

		// 构建大顶堆时,tmp最开始是从后往前数的第一个非叶子结点
		for (; leftChild(i) < length; i = child) {
			child = leftChild(i);

			// 如果右结点大于左结点,child就变为右结点
			// 注意顺序,先判断child的长度,再判断左右子树的大小
			if (child < length - 1 && arr[child] < arr[child + 1]) {
				child++;
			}

			// 如果左或右结点大于父节点,就和父结点交换
			if (arr[child] > arr[i]) {
				swap(arr, i, child);
			} else {
				break;
			}
		}
	}

	/**
	 * @param i
	 * @return 左孩子的位置
	 */
	private static int leftChild(int i) {
		return 2 * i + 1;
	}
}

4、归并排序

归并排序是递归算法一个好的实例,基本操作是合并两个已排序的表。

import java.util.Scanner;

/**
 * 归并排序
 * 合并两个已排序的数组
 * @author thinkpad_ljj
 *
 */
public class MergesSort {

	public static void main(String[] args) {
		Scanner read = new Scanner(System.in);
		int n = read.nextInt();
		int[] arr = new int[n];
		for(int i = 0;i<n;i++) {
			arr[i]=read.nextInt();
		}
		
		Mergesort(arr);
		
		for (int i : arr) {
			System.out.println(i);
		}
	}

	private static void Mergesort(int[] arr) {
		sort(arr,0,arr.length-1);
	}

	/**
	 * 排序,使用递归
	 * @param arr	待排序的数组
	 * @param left	左部分
	 * @param right	右部分
	 */
	private static int[] sort(int[] arr, int left, int right) {
		int mid = (left+right)/2;
		if(left<right) {
			/*左边排序*/
			sort(arr,left,mid);
			/*右边排序*/
			sort(arr,mid+1,right);
			/*合并*/
			merge(arr,left,mid,right);
		}
		return arr;
	}

	/**
	 * 合并数组
	 * 在合并的时候进行排序处理
	 * @param arr
	 * @param left
	 * @param mid
	 * @param right
	 */
	private static void merge(int[] arr, int left, int mid, int right) {
		
		int i=left;
		int j=mid+1;
		int k=0;
		int[] temp = new int[right - left + 1];
		
		for(;i<=mid && j<=right;) {
			if(arr[i]>arr[j]) {
				temp[k++]=arr[j++];
			}else {
				temp[k++]=arr[i++];
			}
		}
		
		/*比较完若数组还剩下运算,就直接加入到新数组当中*/
		while(i<=mid) {
			temp[k++]=arr[i++];
		}
		while(j<=right) {
			temp[k++]=arr[j++];
		}
		
		for(int t = 0;t<temp.length;t++) {
			arr[t+left] = temp[t];
		}
	}
}

5、快速排序

找出关键字,然后进行比较,比关键字大的排在右边,比关键字小的排在左边,这部分排好序后,接着继续分别对左半边以及右半边以同样的方式进行排序

import java.util.Scanner;

/**
 * 快速排序
 * 
 * @author thinkpad_ljj
 *
 */
public class quickSort {

	public static void main(String[] args) {
		Scanner read = new Scanner(System.in);
		int n = read.nextInt();
		int[] arr = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = read.nextInt();
		}

		quickSort(arr, 0, arr.length - 1);

		for (int i : arr) {
			System.out.println(i);
		}
	}

	private static void quickSort(int[] arr, int left, int right) {
		if (left < right) {
			int mid = getMidNum(arr, left, right);
			quickSort(arr, left, mid - 1);
			quickSort(arr, mid + 1, right);
		}
	}

	/**
	 * 获得比较时的关键字
	 * 
	 * @param arr
	 * @param left
	 * @param right
	 * @return
	 */
	private static int getMidNum(int[] arr, int left, int right) {
		/* 数组的第一个数值作为关键字 */
		int temp = arr[left];

		while (left < right) {
			/*要加上约束条件left<right,否则会溢出*/
			while (left < right && arr[right] >= temp) {
				right--;
			}
			arr[left] = arr[right];

			while (left < right && arr[left] < temp) {
				left++;
			}
			arr[right] = arr[left];
		}

		arr[left] = temp;
		return left;
	}
}

6、冒泡排序

每趟只比较两个相邻元素,每次都能把最大的元素“浮”出来。

private static void bubbleSort(int[] arr) {
		int length = arr.length;
		boolean change;

		do {
			change = false;
			for (int i = 0; i < length-1; i++) {
				if (arr[i] > arr[i + 1]) {
					int temp = arr[i];
					arr[i] = arr[i + 1];
					arr[i + 1] = temp;
				}
				change = true;
			}
			length -= 1;
		} while (change);

	}

7、选择排序

基本思想:每次找出待排序数组中最小的数,与待排序数组的第一个数进行交换,接着继续比较,比如3,4 ,2,5,第一次排序就找出最小数2,接着和3进行交换-->2,4,3,5,再找出下一轮待排序中最小值为3,就和4进行交换-->2,3,4,5

/**
	 * 每次找出最小值,与待排序的首个元素进行交换
	 * 
	 * @param arr
	 */
	private static void selectSort(int[] arr) {

		int i;
		int temp;
		int k = 0;

		/* 循环找出最小值 */
		for (i = 0; i < arr.length; i++) {

			k = i;
			/* 循环找出最小值 */
			for (int j = i + 1; j < arr.length; j++) {

				if (arr[j] < arr[k]) {
					k = j;
				}
			}

			/*与待排序的首个元素交换*/
			temp = arr[k];
			arr[k] = arr[i];
			arr[i] = temp;
		}
	}

各排序的时间复杂度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值