基础复习--内排序&查找算法

内排序

  • 内排序,指记录数较少,可以在内存中进行的排序。

  • 排序算法稳定性,排序关键字相同的记录拍完序顺序保持不变,这个算法就是稳定的。

  • 生成一个随机数组

void mkarr(int **arr, int size) {
    *arr = (int*)malloc(sizeof(int)*size);
    srand(time(NULL));
    for (int i = 0; i < size; i++)
        *(*arr + i) = rand();
}
  • 检查排序是否正确
void checkSorted(int *arr, int size, int flag) {
    if (flag == 0) {
        for (int i = 0; i < size - 1; i++)
            if (arr[i] > arr[i + 1]) {
                printf("error index[%d], value1[%d], value2[%d]\n", i, arr[i], arr[i + 1]);
                return;
            }
    }
    else {
        for (int i = 0; i < size - 1; i++)
            if (arr[i] < arr[i + 1]) {
                printf("error index[%d], value1[%d], value2[%d]\n", i, arr[i], arr[i + 1]);
                return;
            }
    }
    printf("all right\n");
}

1.插入排序

假设数组的前i个数已经排好序,然后将第i+1个数插入到前i个中,只到n个数都插入到自己的位置。
平均时间复杂度 O(n2),当序列已经排好序时时间复杂度低O(n),当序列是逆序时时间复杂度最高为O(n2)。插入过程都是从后往前,所以算法是稳定的。

void insertSort(int *arr, int size) {
    for (int i = 1; i < size; i++) {
        int tmp = arr[i];  // 保存第i个元素
        int j = i - 1;
        for (j = i - 1; j >= 0 && tmp < arr[j]; j--) {
            arr[j + 1] = arr[j]; // 比第i个元素小,向后移一位
        }
        arr[j + 1] = tmp;
    }
}

2.选择排序

假设前i-1个元素已经排好序,从第i到n个元素中选择最小的放到第i个位置,只到第n个元素。
因为每次选择都要把剩下的元素全部检查一遍所以,算法的最低、最高、平均时间复杂度都是O(n2)。选择排序在第一轮的最小元素是最后一个,这样第一个元素就直接被交换到最后,如果序列中有和第一个元素相同的元素,那他们的相对位置肯定变了,所以算法是不稳定的。

void selectionSort(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        int min = i;
        int j = i;
        for (; j < size; j++) {
            if (arr[min] > arr[j]) min = j;
        }
        int tmp = arr[i];
        arr[i] = arr[min];
        arr[min] = tmp;
    }
}

3.冒泡排序

从前到后相邻的元素比较大小,如果前面的元素大于后面的,两个元素交换,如果没有大于,下一个元素继续与相邻的下一个元素进行比较,每一轮可以确定一个最大的元素。
每一轮冒泡都要把所有相邻元素进行比较,所以算法的最低、最高、平均时间复杂度都是O(n2)。冒泡方向不变,所以算法是稳定的。

void  bubbleSort(int *arr, int size) {
    int tmp;
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp;
            }
        }
    }
}

4.快排

选择一个基准元素对数组进行分割,比基准大的在一边,比基准小的在一边,分割结束后基准元素已经排好序。再对左右两个部分递归进行分割,直到整个数组有序。
平均时间复杂度O(nlogn),当算法每次选择基准值时都选择到最大或最小元素时,算法时间复杂度最高O(n2),每次都选到中间值时时间复杂度最低为O(nlogn)。划分的过程是从两边向中间走,所以快速排序不稳定。

// 划分数组,返回基准值位置
int parition(int *arr, int size) {
    int pivot = 0;
    int tmp = arr[pivot];
    int low = 0, high = size - 1;
    while (low < high) {
        while (arr[high] > tmp  && low < high)
            high--;
        if (low < high) {
            arr[pivot] = arr[high]; pivot = high;
        }
        while (arr[low] < tmp && low < high)
            low++;
        if (low < high) {
            arr[pivot] = arr[low]; pivot = low;
        }
    }
    arr[pivot] = tmp;
    return pivot;  //返回基准元素的位置
}
void quickSort(int *arr, int size) {
    if (size <= 1) return;
    if (size == 2 && arr[0] > arr[1]) {
        int tmp = arr[0]; arr[0] = arr[1]; arr[1] = tmp;
    }
    int pivot = parition(arr, size);
    quickSort(arr, pivot);
    quickSort(arr + pivot + 1, size - pivot - 1);
}

5.归并排序

把两个相对有序的子序列合并成一个.
时间复杂度 O(nlogn),空间复杂度 n。两个序列归并的过程相同排序字段的元素不会发生交换,所以是稳定的。

// 合并两个子序列
void merge(int *arr, int *res, int start1, int end1, int start2, int end2) {
    int ix = start1;
    // 先复制到临时数组,然后再从临时数组往回合并
    for (int i = start1; i <= end2; i++)  res[i] = arr[i];

    while (start1 <= end1 && start2 <= end2) {
        if (res[start1] <= res[start2])
            arr[ix++] = res[start1++];
        else
            arr[ix++] = res[start2++];
    }
    while (start1 <= end1)
        arr[ix++] = res[start1++];
    while (start2 <= end2)
        arr[ix++] = res[start2++];

}
void mergeSort(int *arr, int *res, int start, int end) {
    if (start >= end)
        return;
    mergeSort(arr, res, start, (end + start) / 2);
    mergeSort(arr, res, (end + start) / 2 + 1, end);
    merge(arr, res, start, (end + start) / 2, (end + start) / 2 + 1, end);
}

6.堆排序

对数组的前n个元素建堆,将第一个元素与第n个元素交换,然后对前n-1元素建堆.
重新建堆的时间复杂度为O(logn),所以算法的最好、最坏、平均时间复杂度都O(nlogn)。建堆过程中排序字段相同元素的父节点可能不相同,导致相对顺序有可能改变,所以堆排序不稳定。

void heapSort(int* arr, int start, int end) {
    int tmp;
    // 对从start 到end 建立大根堆
    while (end > start) {
        for (int i = (start + end - 1) / 2; i >= 0; i--) {
            if (arr[i] < arr[i * 2 + 1]) {
                tmp = arr[i]; arr[i] = arr[i * 2 + 1]; arr[i * 2 + 1] = tmp;
            }
            if (i * 2 + 2 <= end && arr[i] < arr[i * 2 + 2]) {
                tmp = arr[i]; arr[i] = arr[i * 2 + 2]; arr[i * 2 + 2] = tmp;
            }
        }
        // 最大的元素与最后一个元素交换,然后继续对start到end-1建立大根堆
        tmp = arr[start]; arr[start] = arr[end]; arr[end] = tmp;
        end--;
    }
}

7.shell排序

先用增量x对原数组进行划分,(0,x,2*x…)/(1,1+x,1+2*x…)/(2,2+x,2+2*x…)…
然后在每个划分上执行插入排序,然后增量减半x=x/2,继续执行,知道x=1;
与插入排序相同,当原序列有序是时间复杂度最低为O(n),平均和最高时间复杂度都为O(n2)。相同排序关键字的元素可能会划入不同的子序列,导致相对位置变化,所以shell排序是不稳定的。

void shellInsertSort(int *arr, int start, int end, int step) {
    int tmp; int j;
    for (int i = start + step; i <= end; i++) {
        tmp = arr[i];
        for (j = i - step; j >= start && arr[j] > tmp; j -= step)
            arr[j + step] = arr[j];
        arr[j + step] = tmp;
    }
}
void shellSort(int* arr, int start, int end) {
    int tmp = 0;
    for (int i = (start + end + 1) / 2; i >= 1; i /= 2) { // 步长从 序列长度的二分之一开始, 等于一时变成和普通的插入排序一样
        for (int j = start; j < i; j++) {
            // 在每个划分上执行插入排序
            shellInsertSort(arr, j, end, i);
        }
    }
}

查找

8.顺序查找

时间复杂度O(n)

int linerSearch(int *arr, int target, int start, int end) {
    while (start <= end) {
        if (arr[start] == target)
            return start;
        start++;
    }
    return -1; // 查找失败
}
9.二分查找

假设原序列有序(递增),先查找序列中间的元素,如果相等返回坐标,如果小于目标则继续在后一半中进行二分查找,大于时在前一半进行二分查找.

时间复杂度O(logn)

int binSearch(int *arr, int target, int start, int end) {
    if (start > end)
        return -1;
    int mid = (start + end) / 2;
    if (arr[(start + end) / 2] == target)
        return (start + end) / 2;
    if (arr[(start + end) / 2] < target)
        return binSearch(arr, target, (start + end) / 2 + 1, end);
    return binSearch(arr, target, start, (start + end) / 2 - 1);
}

你可能感兴趣的:(排序算法,算法,二分查找)