基本原则
1、如果运行时间是常数量级,用常数1表示;
2、只保留时间函数中的最高阶项;
3、如果最高阶项存在,则省去最高阶项前面的系数。
的判断
“运行时间中的对数”的一般法则规定:
如果一个算法用常数时间(O(1)将问题的大小削减为其一部分(通常是1/2),那么该算法就是 O(log N)。 另一方面,如果使用常数时间只是把问题减少一个常数(如将问题减少1),那么这种算法就是 O(N) 。
递归问题的判断
斐波那契数列
long aFunc(int n)
{
  if (n<=1) return 1;
  else return aFunc(n-1)+aFunc(n-2));
显然运行次数,T(0) = T(1) = 1,同时 T(n) = T(n - 1) + T(n - 2) + 1,这里的 1 是其中的加法算一次执行。 显然 T(n) = T(n - 1) + T(n - 2) 是一个斐波那契数列
这里,给定规模 n,计算 Fib(n) 所需的时间为计算 Fib(n-1) 的时间和计算 Fib(n-2) 的时间的和。
T(n<=1) = O(1)
T(n) = T(n-1) + T(n-2) + O(1)
                 fib(5)   
             /             \     
       fib(4)                fib(3)   
     /      \                /     \
 fib(3)      fib(2)         fib(2)    fib(1)
/     \        /    \       /    \  
也就是说,计算了个fib(1)。
简单规则
1.只关注循环执行次数最多的一段代码
2.加法法则:总复杂度是量级最大的那段代码的复杂度
如果 T1(n)=O(f(n)),T2(n)=O(g(n)); 那么 T(n) = T1(n)+T2(n) = max(O(f(n)), O(g(n))) = O(max(f(n), g(n)))
3.乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
4.不考虑非浮点数计算的元素大小比较语句
一般循环的判断,计数,以及if 的比较操作,不计算在内
十大排序算法的最坏时间复杂度分析
参考
https://www.runoob.com/w3cnote/ten-sorting-algorithm.html
https://www.inf.hs-flensburg.de/lang/algorithmen/sortieren/index.htm
冒泡排序
#include <iostream>
using namespace std;
template<typename T> //整数或浮点数皆可使用,若要使用类(class)或结构体(struct)时必须重载大于(>)运算符
void bubble_sort(T arr[], int len) {
        int i, j;
        for (i = 0; i < len - 1; i++)
                for (j = 0; j < len - 1 - i; j++)
                        if (arr[j] > arr[j + 1])
                                swap(arr[j], arr[j + 1]);
}
int main() {
        int arr[] = { 61, 17, 29, 22, 34, 60, 72, 21, 50, 1, 62 };
        int len = (int) sizeof(arr) / sizeof(*arr);
        bubble_sort(arr, len);
        for (int i = 0; i < len; i++)
                cout << arr[i] << ' ';
        cout << endl;
        float arrf[] = { 17.5, 19.1, 0.6, 1.9, 10.5, 12.4, 3.8, 19.7, 1.5, 25.4, 28.6, 4.4, 23.8, 5.4 };
        len = (float) sizeof(arrf) / sizeof(*arrf);
        bubble_sort(arrf, len);
        for (int i = 0; i < len; i++)
                cout << arrf[i] << ' '<<endl;
        return 0;
}
找出算法运行最多的代码swap(arr[j], arr[j + 1]);运行了次。则时间复杂度为
选择排序
template<typename T> //整數或浮點數皆可使用,若要使用物件(class)時必須設定大於(>)的運算子功能
void selection_sort(std::vector<T>& arr) {
        for (int i = 0; i < arr.size() - 1; i++) {
                int min = i;
                for (int j = i + 1; j < arr.size(); j++)
                        if (arr[j] < arr[min])
                                min = j;
                std::swap(arr[i], arr[min]);
        }
}
找出算法运行最多的代码std::swap(arr[i], arr[min]);运行了次。则时间复杂度为
插入排序
void insertion_sort(int arr[],int len){
        for(int i=1;i<len;i++){
                int key=arr[i];
                int j=i-1;
                while((j>=0) && (key<arr[j])){
                        arr[j+1]=arr[j];
                        j--;
                }
                arr[j+1]=key;
        }
}
找出算法运行最多的代码arr[j+1]=arr[j];运行了次。则时间复杂度为
希尔排序
希尔排序是插入排序的改进,基于如果插入排序基本有序的情况下,排序速度会变得很快的原理,根据某个增量序列进行迭代,第一次迭代将序列分为个部分进行排序,在迭代在至,此时由于数据基本有序,所以使用插入排序会使得排序速度很快。
希尔排序的最坏时间复杂度和增量序列的设置有关,如果是则为,如果是则为,如果是Sedgewick提出的 {1, 5, 19, 41, 109,...}则为. 希尔排序不稳定,适用于5000左右的小数据排序,此外在数据存在一定有序性的情况下,排序会更快。
void shellsort (int[] a, int n)
{
    int i, j, k, h, v;
    int[] cols = {1391376, 463792, 198768, 86961, 33936, 13776, 4592,
                    1968, 861, 336, 112, 48, 21, 7, 3, 1}
    for (k=0; k<16; k++)
    {
        h=cols[k];
        for (i=h; i<n; i++)
        {
            v=a[i];
            j=i;
            while (j>=h && a[j-h]>v)
            {
                a[j]=a[j-h];
                j=j-h;
            }
            a[j]=v;
        }
    }
}
归并排序
递归的归并排序可以得到递归的二叉树,层数是,每层有个单元,第层每个单元进行1次基本运算, 第层每个单元进行(4-2)次基本运算,第1层进行(n-2)次基本运算。因此总共进行次运算,共有个加数,因此最坏和最优时间复杂度都为
//递归版
void Merge(vector<int> &Array, int front, int mid, int end) {
    // preconditions:
    // Array[front...mid] is sorted
    // Array[mid+1 ... end] is sorted
    // Copy Array[front ... mid] to LeftSubArray
    // Copy Array[mid+1 ... end] to RightSubArray
    vector<int> LeftSubArray(Array.begin() + front, Array.begin() + mid + 1);
    vector<int> RightSubArray(Array.begin() + mid + 1, Array.begin() + end + 1);
    int idxLeft = 0, idxRight = 0;
    LeftSubArray.insert(LeftSubArray.end(), numeric_limits<int>::max());
    RightSubArray.insert(RightSubArray.end(), numeric_limits<int>::max());
    // Pick min of LeftSubArray[idxLeft] and RightSubArray[idxRight], and put into Array[i]
    for (int i = front; i <= end; i++) {
        if (LeftSubArray[idxLeft] < RightSubArray[idxRight]) {
            Array[i] = LeftSubArray[idxLeft];
            idxLeft++;
        } else {
            Array[i] = RightSubArray[idxRight];
            idxRight++;
        }
    }
}
void MergeSort(vector<int> &Array, int front, int end) {
    if (front >= end)
        return;
    int mid = (front + end) / 2;
    MergeSort(Array, front, mid);
    MergeSort(Array, mid + 1, end);
    Merge(Array, front, mid, end);
}
快速排序
快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
//严蔚敏《数据结构》标准分割函数
 Paritition1(int A[], int low, int high) {
   int pivot = A[low];
   while (low < high) {
     while (low < high && A[high] >= pivot) {
       --high;
     }
     A[low] = A[high];
     while (low < high && A[low] <= pivot) {
       ++low;
     }
     A[high] = A[low];
   }
   A[low] = pivot;
   return low;
 }
 void QuickSort(int A[], int low, int high) //快排母函数
 {
   if (low < high) {
     int pivot = Paritition1(A, low, high);
     QuickSort(A, low, pivot - 1);
     QuickSort(A, pivot + 1, high);
   }
 }



京公网安备 11010502036488号