Java数据结构和算法
冒泡排序BubbleSort
冒泡排序(Bubble Sorting)的基本思想:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部。
优化:因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来,没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。
import java.util.Arrays;
/**
*
* @Description 冒泡排序,O(n^2)
* @author 岳昌宏
* @version
* @data 2022年2月8日上午11:44:12
*/
public class BubbleSort {
public static void main(String[] args) {
int[] arr = new int[] {3, 2, 5, 1, 6, 2, 4};
boolean flag = false;
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]) {
flag = true;
arr[j] = arr[j] ^ arr[j+1];
arr[j+1] = arr[j] ^ arr[j+1];
arr[j] = arr[j] ^ arr[j+1];
}
}
if (!flag) { // 在一次排序中,一次交换都没有发生
break;
}else {
flag = false;
}
}
System.out.println(Arrays.toString(arr));
}
}
选择排序SelectSort
选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选择出某一个元素,再依规定交换位置后达到排序的目的。
选择排序(select sort)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]arr[n-1]中选取最小值,与arr[1]交换…总共通过n-1次,得到一个按排序码从小到大排序的有序序列。
import java.util.Arrays;
/**
*
* @Description 选择排序, O(n^2)
* @author 岳昌宏
* @version
* @data 2022年2月8日下午6:09:22
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = new int[] {1, 4, 12, 76 ,2, -2};
for (int i = 0; i < arr.length - 1; i++) {
int mindex = i; // 记录最小值下标
int min = arr[i]; // 记录最小值
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < min) {
min = arr[j];
mindex = j;
}
}
if (mindex != i) {
arr[mindex] = arr[i];
arr[i] = min;
}
}
System.out.println(Arrays.toString(arr));
}
}
插入排序InsertionSort
插入排序(Insertion Sorting)的基本思想:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
import java.util.Arrays;
/**
*
* @Description 插入排序
* @author 岳昌宏
* @version
* @data 2022年2月9日上午10:13:43
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = new int[] {1, 4, 12, 76 ,2, -2};
for (int i = 1; i < arr.length; i++) { // 从第二位开始插入
int insertNumber = arr[i]; // 待插入数
int insertIndex = i-1; // 有序列表最大下标
while (insertIndex >= 0 && arr[insertIndex] > insertNumber) {
arr[insertIndex + 1] = arr[insertIndex]; // 向前挪一位
insertIndex--; // 判断下一位
}
arr[insertIndex + 1] = insertNumber;
}
System.out.println(Arrays.toString(arr));
}
}
希尔排序ShellSort
希尔排序是希尔于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减少至1时,整个文件恰被分为一组,算法便终止。
import java.util.Arrays;
/**
*
* @Description 希尔排序 -- 交换法
* @author 岳昌宏
* @version
* @data 2022年2月9日上午11:17:43
*/
public class ShellSort {
public static void main(String[] args) {
int[] arr = new int[] {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
for (int group = arr.length / 2 ; group > 0; group = group / 2) { // 分组
for (int i = group; i < arr.length; i++) {
for (int j = i - group; j >= 0; j = j -group) {
if (arr[j] > arr[j+group]) {
arr[j] = arr[j] ^ arr[j+group];
arr[j+group] = arr[j] ^ arr[j+group];
arr[j] = arr[j] ^ arr[j+group];
}
}
}
}
System.out.println(Arrays.toString(arr));
}
}
import java.util.Arrays;
/**
*
* @Description 希尔排序 --- 位移法
* @author 岳昌宏
* @version
* @data 2022年2月9日上午11:17:43
*/
public class ShellSort {
public static void main(String[] args) {
int[] arr = new int[] {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
for (int group = arr.length / 2; group > 0; group = group / 2) {
for (int i = group; i < arr.length; i++) {
int j = i; // 要插入元素的下标
int temp = arr[j]; // 要插入的元素
while (j - group >= 0 && temp < arr[j - group]) {
// 移动
arr[j] = arr[j- group];
j = j - group;
}
arr[j] = temp;
}
}
System.out.println(Arrays.toString(arr));
}
}
快速排序QucikSort_递归
快速排序是对冒泡排序的一种改进,基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据小,然后再按此方法对两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
import java.util.Arrays;
/**
*
* @Description 快速排序
* @author 岳昌宏
* @version
* @data 2022年2月9日下午2:30:34
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[] {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int left, int right) {
int l = left; // 左下标
int r = right; // 右下标
int pivot = arr[(left + right) / 2]; // 中间值
while (l < r) {
// 在pivot的左边一直找,找到大于等于pivot的值,才退出
while(arr[l] < pivot) {
l++;
}
while(arr[r] > pivot) {
r--;
}
/**
* 出现这种可能是,pivot的左边的值都比pivot的小,l找到了pivot的右边
*/
if (l >= r) {
break;
}
// 交换
arr[l] = arr[l] ^ arr[r];
arr[r] = arr[l] ^ arr[r];
arr[l] = arr[l] ^ arr[r];
/**
* 思考思考:设计很巧妙
* 放置进入死循环
*/
// 如果交换完后,发现arr[l] == pivot值
if (arr[l] == pivot) {
r--;
}
if (arr[r] == pivot) {
l++;
}
}
/**
* 如果l == r
*/
if (l == r) {
l++;
r--;
}
/**
* 向左递归
*/
if (left < r) {
quickSort(arr, left , r);
}
/**
* 向右递归
*/
if (right > l) {
quickSort(arr, l, right);
}
}
}
归并排序MergeSort_递归
import java.util.Arrays;
/**
*
* @Description 归并排序
* @author 岳昌宏
* @version
* @data 2022年2月9日下午8:59:22
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = new int[] {8, 4, 5, 7, 1, 3, 6, 2};
mergeSort(arr, 0, arr.length-1, new int[arr.length]);
System.out.println(Arrays.toString(arr));
}
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2; // 中间索引
// 向左递归进行分解
mergeSort(arr, left, mid, temp);
// 向右递归进行分解
mergeSort(arr, mid+1, right, temp);
// 合并
merge(arr, left, mid, right, temp);
}
}
/**
* 合并的方法
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 中转的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; // 初始化i,左边有序队列的初始索引
int j = mid + 1; // 初始化j,右边有序序列的初始索引
int t = 0; // 指向temp数组的当前索引
// 1.先把左右两端的数据按照规则填充到temp中,直到左右两边有序序列有一边处理完毕为止
while(i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
t++;
i++;
}else {
temp[t] = arr[j];
t++;
j++;
}
}
// 2.把有剩余数据的一边的数据依次全部到填充到temp去
while(i <= mid) {
temp[t] = arr[i];
i++;
t++;
}
while(j <= right) {
temp[t] = arr[j];
j++;
t++;
}
// 3.将temp数组拷贝到arr
t = 0;
int tempLeft = left;
while(tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
}
基数排序(桶排序)
基本思想:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成了一个有序序列。稳定性,如果有负数,需要对代码进行改变
import java.util.Arrays;
/**
*
* @Description 基数排序
* @author 岳昌宏
* @version
* @data 2022年2月10日上午11:31:20
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = new int[] {53, 3, 542, 748, 14, 214};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int[] arr) {
// 定义一个二维数组,表示10个桶
int[][] bucket = new int[10][arr.length];
// 为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶每次放入的个数
int[] bucketElementCounts = new int[10];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
for (int i = 0, n = 1; i < String.valueOf(max).length() ; i++, n = n * 10) {
for (int j = 0; j < arr.length; j++) {
int digitOfElement = arr[j] / n % 10; // 位
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
int index = 0;
for (int k = 0; k < bucketElementCounts.length; k++) {
if (bucketElementCounts[k] != 0) {
for (int l = 0; l < bucketElementCounts[k]; l++) {
// 取出元素
arr[index] = bucket[k][l];
index++;
}
}
bucketElementCounts[k] = 0;
}
}
}
}
堆排序
import java.util.Arrays;
/**
*
* @Description 堆排序
* @author 岳昌宏
* @version
* @data 2022年2月17日下午5:45:05
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = new int[] {4, 6, 8, 5, 9};
headSort(arr);
System.out.println(Arrays.toString(arr));
}
// 堆排序
public static void headSort(int[] arr) {
for (int i = arr.length / 2-1; i >=0; i--) { // 非叶子节点
adjustHeap(arr, i, arr.length);
}
for(int j = arr.length-1; j > 0; j--) {
// 交换
arr[0] = arr[j] ^ arr[0];
arr[j] = arr[j] ^ arr[0];
arr[0] = arr[j] ^ arr[0];
adjustHeap(arr, 0, j);
}
}
// 将一个数组(二叉树),调整成一个大顶堆
/**
*
* @param arr 待调整的数组
* @param i 非叶子节点在数组中的索引
* @param length 对多少个元素进行调整,length是在逐渐减少的
*/
public static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i]; // 先取出当前临时变量的值
// 开始调整
for (int k = i*2+1; k < length; k = k*2+1) {
if (k+1 < length && arr[k] < arr[k+1]) { // 说明左子节点的值小于右子节点的值
k++; // k就指向右子节点
}
if (arr[k] > temp) { // 如果子节点大于父节点
arr[i] = arr[k]; // 把较大的值赋给当前节点
i = k; // 让i指向k,继续循环比较
}else {
break;
}
}
// 当for循环结束宏,我们已经将i为父节点的树的最大值,放在了最顶上
arr[i] = temp;
}
}