概念:以某个值或值的集合为输入,将输入转换成输出的计算步骤的一个序列。
O(1)<O(logn)<O(n)<O(nlogn)<O(n*n)
算法的时间复杂度用来衡量随问题规模n的增长执行次数的增长率。
常见排序:
插入排序
遍历数组,遍历到i时,a0,a1...ai-1是已经排好序的,取出ai,从ai-1开始从后向前和每个比较大小(从前向后比的话需要考虑移动其他元素的问题),如果小于,则将此位置元素向后移动,继续先前比较,如果不小于,则放到正在比较的元素之后。可见相等元素比较是,原来靠后的还是拍在后边,所以插入排序是稳定的。
冒泡排序
冒泡排序的名字很形象,实际实现是相邻两节点进行比较,大的向后移一个,经过第一轮两两比较和移动,最大的元素移动到了最后,第二轮次大的位于倒数第二个,依次进行。这是最基本的冒泡排序,还可以进行一些优化。
优化一:如果某一轮两两比较中没有任何元素交换,这说明已经都排好序了,算法结束,可以使用一个Flag做标记,默认为false,如果发生交互则置为true,每轮结束时检测Flag,如果为true则继续,如果为false则返回。
优化二:某一轮结束位置为j,但是这一轮的最后一次交换发生在lastSwap的位置,则lastSwap到j之间是排好序的,下一轮的结束点就不必是j--了,而直接到lastSwap即可,代码如下:
归并排序
时间复杂度O(nlogn)
public static void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
System.out.println("temp:" + Arrays.toString(temp));
int i = low;// 左指针
int j = mid + 1;// 右指针
int k = 0;
// 把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
System.out.println("把较小的数先移到新数组中1:" + Arrays.toString(temp));
System.out.println("k:"+k);
} else {
temp[k++] = a[j++];
System.out.println("把较小的数先移到新数组中2:" + Arrays.toString(temp));
System.out.println("k:"+k);
}
}
// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = a[i++];
System.out.println("把左边剩余的数移入数组:" + Arrays.toString(temp));
}
// 把右边边剩余的数移入数组
System.out.println("j:"+j);
while (j <= high) {
temp[k++] = a[j++];
System.out.println("把右边边剩余的数移入数组:" + Arrays.toString(temp));
}
// 把新数组中的数覆盖nums数组
for (int k2 = 0; k2 < temp.length; k2++) {
a[k2 + low] = temp[k2];
}
}
public static void mergeSort(int[] a, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
mergeSort(a, low, mid);
// 右边
mergeSort(a, mid + 1, high);
// 左右归并
merge(a, low, mid, high);
System.out.println(Arrays.toString(a));
}
}
public static void main(String[] args) {
int a[] = { 51, 46, 20, 18};
mergeSort(a, 0, a.length - 1);
System.out.println("排序结果:" + Arrays.toString(a));
}
一.其实就是数学里的必用习惯:直接表示法。
1.将原始问题原子性得表达为不可再拆解得独立子问题,一一映射到逻辑步骤里。(有几个子问题就有几个步骤)
2.把用到的不同含义的变量(此题只把循环用到的表示出来了)独立表示出来如左边数组的第一位最后一位,右边数组的第一位最后一位
package test.MainTEST;
import java.util.Arrays;
import static test.MainTEST.TestRunTimeSelfException.INSTANCEOne;
public class MainTest {
public static void main(String[] args) {
//测试是否能捕获throw
MainTest main = new MainTest();
//System.out.println(INSTANCEOne.getCode());
/*归并排序*/
int[] a = {54,52,30, 20,10,5,98,67,69};
int length = a.length;
main.merge(a, 0, length / 2 - 1, length / 2, length - 1, length);
System.out.println("结果数组" + Arrays.toString(a));
}
private int[] merge(int[] a, int leftFirst, int leftLast, int rightFirst, int rightLast, int len) {
int[] temp = new int[len];
if (len == 1) {
return a;
}
int leftLen = leftLast - leftFirst + 1;
if(leftLen>=2) {
//子左分割,归并
int sonLeftLast = leftFirst + leftLen/2 -1;
int sonrightFirst = sonLeftLast + 1;
this.merge(a, leftFirst, sonLeftLast, sonrightFirst, leftLast, leftLen);
}
int rightLen = len - leftLen;
if(rightLen>=2){
//子右分割,归并
int sonLeftLast = rightFirst + rightLen / 2 - 1;
int sonrightFirst = sonLeftLast + 1;
this.merge(a, rightFirst, sonLeftLast, sonrightFirst, rightLast, rightLen);
}
//归并最外层
int i = leftFirst,j=rightFirst,k=0;
while (i <= leftLast & j <= rightLast)
{
if (a[i] <= a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
//左边剩余的处理(此时右边刚好处理完)
while (i <= leftLast)
{
temp[k++] = a[i++];
}
//右边的剩余处理(此时左边刚好处理完)
while (j <= rightLast)
{
temp[k++] = a[j++];
}
for (int m = 0; m < temp.length; m++) {
a[leftFirst++] = temp[m];
}
System.out.println("temp数组:"+Arrays.toString(temp));
System.out.println("每归并一次:" + Arrays.toString(a));
return a;
}
}
快速排序
二叉树的时间复杂度
平衡二叉排序树深度为Log2|n+1,时间复杂度为O(Log2|n)
如果二叉排序树完全不平衡,则二叉排序树的深度为n,退化为顺序查找,时间复杂度为O(n)。
综上,二叉排序树的时间复杂度介于O(Log2|n)和O(n)之间。因此为了提高查询效率,二叉排序树最好构造成平衡的。