1. Java排序:冒泡排序 - 最简单
(1)比较前后相邻的二个数据,如果前面数据大于后面的数据,就将这二个数据交换。
(2)这样对数组的第 0 个数据到 N-1 个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
(3)N=N-1,如果 N 不为 0 就重复前面二步,否则排序完成。
【逻辑】外层0 ~
。相邻元素,比较大小,互换位置
【优点】稳定
【缺点】慢,每次只能移动相邻两个数据
public class TestArraySort{
public static void main(String[] args) {
// Test 冒泡
int[] nums = {1, 8, 3, 6, 2, 9, 7, 4, 5};
bubbleSort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + " "); // 1 2 3 4 5 6 7 8 9
}
}
/**
* Java排序:冒泡排序
* 说明:相邻元素,比较大小,互换位置
* @param array 需要排序的无序整数类型数组
*/
public static void bubbleSort(int[] array) {
// 1. 参数合法性判断
if (null == array || array.length == 0) {
return;
}
// 2. 冒泡排序主逻辑
for (int i = 0; i < array.length-1; i++) {
for (int j = 0; j < array.length-1 - i; j++) {
if (array[j] > array[j+1]) { // 相邻元素,升序
//if (array[j] < array[j+1]) { // 相邻元素,降序
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
}
}
2. Java排序:插入排序 - 简单
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。
插入排序非常类似于整扑克牌。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。
如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数。如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是(n2)。
【逻辑】外层1 ~
。第2个数开始,与前面的数逐个比较,插入位置
【优点】稳定,快
【缺点】比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候
public class TestArraySort{
public static void main(String[] args) {
int[] nums = {1, 8, 3, 6, 2, 9, 7, 4, 5};
insertSort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + " "); // 1 2 3 4 5 6 7 8 9
}
}
/**
* Java排序:插入排序
* 说明:第2个数开始,与前面的数逐个比较,插入位置
* @param array 需要排序的无序整数类型数组
*/
public static void insertSort(int[] array) {
// 1. 参数合法性判断
if (null == array || array.length == 0) {
return;
}
// 2. 插入排序主逻辑
for (int i = 1; i < array.length; i++) { // 从第2个数temp开始
for (int j = i; j > 0; j--) { // temp每次与前面的数比较,插入进去
if (array[j] < array[j-1]) { // 升序
//if (array[j] < array[j-1]) { // 降序
int temp = array[j];
array[j] = array[j-1];
array[j-1] = temp;
}
}
}
}
}
3. Java排序:选择排序 - 简单
选择排序(Selection sort)是一种简单直观的排序算法。
它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
选择排序是不稳定的排序方法。
原因是用数组实现的选择排序是不稳定的,用链表实现的选择排序是稳定的,因为数组内会破坏相同元素的位置,所以选择排序是不稳定的。
在《算法》第四版217页上作者已经说了,有很多办法可以将任意排序算法变成稳定的,但是,往往需要额外的时间或者空间。
【逻辑】外层0 ~
。固定值与其他值依次比较,互换位置
【优点】数据移动次数已知为(n-1)次
【缺点】比较次数多
public class TestArraySort {
public static void main(String[] args) {
int[] nums = {1, 8, 3, 6, 2, 9, 7, 4, 5};
selectSort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + " "); // 1 2 3 4 5 6 7 8 9
}
}
/**
* Java排序:选择排序
* 说明:固定值与其他值依次比较,互换位置
* @param array 需要排序的无序整数类型数组
*/
public static void selectSort(int[] array) {
// 1. 参数合法性判断
if (null == array || array.length == 0) {
return;
}
// 2. 选择排序主逻辑
for (int i = 0; i < array.length-1; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) { // 升序:选择1个数array[i]与i+1后面的数依次比较
//if (array[i] < array[j]) { // 降序
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
}
}
}
4. Java排序:快速排序 - 递归(难)
快速排序的原理:选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。
一次循环:从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有继续比较下一个,直到找到第一个比基准值小的值才交换。找到这个值之后,又从前往后开始比较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的值才交换。直到从前往后的比较索引 > 从后往前比较的索引,结束第一次循环,此时,对于基准值来说,左右两边就是有序的了。
【逻辑】选择第一个数位基准值,开始首尾比较,首[0]基准值开始的i++找 ≤ 首值的进行交换,尾[length-1]基准值开始的j--找 ≥ 尾值的进行交换,找到中间后,两半边各自递归
【优点】极快,数据移动少
【缺点】不稳定(原因同选择排序)
public class TestArraySort {
public static void main(String[] args) {
int[] nums = {1, 8, 3, 6, 2, 9, 7, 4, 5};
quickSort(nums, 0, nums.length-1);
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + " "); // 1 2 3 4 5 6 7 8 9
}
}
/**
* Java排序:快速排序
* 说明:选一个数作为基数(一般为array[0]),大于基数的放到右边,小于这个基数的放到左边
* @param array 需要排序的无序整数类型数组
* @param low 最小基准位置,eg:0
* @param high 最大基准位置,eg:length-1
*/
public static void quickSort(int[] array, int low, int high) {
// 1. 参数合法性判断
if (null == array || array.length == 0) {
return;
}
if (low > high) {
return;
}
// 2. 快速排序主逻辑
int i, j, temp, t;
i = low;
j = high;
temp = array[low]; // temp就是基准位
while (i < j) {
while (temp <= array[j] && i < j) { // 升序:先看右边,依次往左递减
//while (temp >= array[j] && i < j) { // 降序:先看右边,依次往左递递增
j--;
}
while (temp >= array[i] && i < j) { // 升序:再看左边,依次往右递增
//while (temp <= array[i] && i < j) { // 降序:再看左边,依次往右递减少
i++;
}
if (i < j) { // 如果满足条件则交换
t = array[j];
array[j] = array[i];
array[i] = t;
}
}
// 最后将基准为与i和j相等位置的数字交换
array[low] = array[i];
array[i] = temp;
quickSort(array, low, j - 1); // 递归调用左半边数组
quickSort(array, j + 1, high); // 递归调用右半边数组
}
}
5. Java算法:二分查找 - 升降序逻辑处理
又叫折半查找,要求待查找的序列有序。
默认升序逻辑说明:每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分循环这个查找的过程。直到查找到了为止,否则序列中没有待查的关键字。
- 示例代码:(严谨的判断、有序,升,降序的处理)
public class TestBinarySearch {
public static void main(String[] args) {
int result = 0;
// Test 升序
int[] nums1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
result = binarySearch(nums1, 3);
System.out.println(result); // 2
// Test 降序
int[] nums2 = {9, 8, 7, 6, 5, 4, 3, 2, 1};
result = binarySearch(nums2, 3);
System.out.println(result); // 6
// Test 无序
int[] nums3 = {1, 8, 3, 6, 2, 9, 7, 4, 5};
result = binarySearch(nums3, 3);
System.out.println(result); // -1
}
/**
* Java算法:二分查找/折半查找
* 说明:默认有序,但需判断知道其为升序还是降序
* @param array 被查找的数组,要求默认有序
* @param a 需要查找的数
* @return int 下标位置
*/
public static int binarySearch(int[] array, int a) {
// 1. 参数合法性判断
if (null == array || array.length == 0) {
return -1;
}
// 2. 判断是否有序,以及升序 or 降序
boolean up = false;
for (int i = 0; i < array.length - 1; i++) {
if (array[i] < array[i+1]) {
if (i+1 == array.length-1) { // 遍历到最大下标才算
up = true; // 降序
}
} else if (array[i] > array[i + 1]) {
if (i+1 == array.length - 1) { // 遍历到最大下标才算
up = false; // 升序
}
} else {
return -1;
}
}
// 3. 二分查找主逻辑循环
int minIndex = 0;
int maxIndex = array.length - 1;
int midIndex;
while (minIndex <= maxIndex) { // maxIndex/minIndex动态变化,折半遍历
midIndex = (minIndex + maxIndex) / 2;
if (array[midIndex] > a) {
if (up) {
maxIndex = midIndex-1; // 升序左移
} else {
minIndex = midIndex+1; // 降序右移
}
} else if (array[midIndex] < a) {
if(up) {
minIndex = midIndex+1; // 升序右移
} else {
maxIndex = midIndex-1; // 降序左移
}
} else { // array[midIndex] == a
return midIndex;
}
}
return -1;
}
}