算法基础一排序

认识时间复杂度

常数时间的操作

一个操作如果和样本的数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。

时间复杂度为一个算法流程中,常数操作数量的一个指标。常用O(读作big O)来表示。具体来说,先要对一个算法流程非常熟悉,然后去写出这个算法流程中,发生了多少常数操作,进而总结出常数操作数量的表达式。

在表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果为f(N),那么时间复杂度为O(f(N))。

评价一个算法流程的好坏,先看时间复杂度的指标,然后再分析不同数据样本下的实际运行时间,也就是“常数项时间”。

选择排序、冒泡排序细节与复杂度分析

时间复杂度O(N^2),额外空间复杂度O(1)

选择排序

它的基本思想是每次从未排序的部分中选择最小(或最大)的元素,放到已排序部分的末尾。

 public class selectionSort {
    public static void swap(int arr[], int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void selection(int arr[]) {
        int len = arr.length;

        for (int i = 0; i < len; i++) {
            int tempMaxInt = i;//假设未排序部分的最左端是最大值
            for (int j = i + 1; j < len; j++) {
                if (arr[tempMaxInt] < arr[j]) {
                    tempMaxInt = j;//遍历未排序部分,发现更大的值,记录下最大的值索引
                }
            }
            swap(arr, tempMaxInt, i);//把未排序部分得最大值和已排序部分得下一个,也即是未排序部分得第一个交换
            //然后排序部分向右扩
        }
    }

    public static void main(String args[]) {
        int arr[] = { 5, 4, 1, 3, 2 };
        selection(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

 冒泡排序

 public class bubbleSort {
    public static void swap(int arr[], int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void _bubbleSort(int arr[]) {
        int len = arr.length;
        for (int i = 0; i < len; i++) {
            boolean flag = false;
            for (int j = len - 1; j > i; j--) {
                if (arr[j] > arr[j - 1]) {
                    swap(arr, j - 1, j);
                    flag = true;
                }
            }
            if (!flag) {
                break;
            }
        }
    }

    public static void main(String[] args) {
        int arr[] = { 5, 4, 1, 3, 2 };
        _bubbleSort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

 

 

插入排序

 它的核心思想是将未排序部分的元素逐个插入到已排序部分的正确位置。

 public class insertSort {

    // 插入排序方法
    public static void insertionSort(int[] arr) {
        int n = arr.length;

        // 从第二个元素开始,逐个插入到已排序部分
        for (int i = 1; i < n; i++) {
            int key = arr[i]; // 当前需要插入的元素
            int j = i - 1;

            // 将比 key 大的元素向后移动
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }

            // 插入 key 到正确位置
            arr[j + 1] = key;
        }
    }

    // 打印数组的方法
    public static void printArray(int[] arr) {
        for (int num : arr) {
            System.out.print(num + " ");
        }
        System.out.println();
    }

    // 主方法:测试插入排序
    public static void main(String[] args) {
        int[] arr = { 12, 11, 13, 5, 6 }; // 待排序的数组

        System.out.println("排序前的数组:");
        printArray(arr);

        // 调用插入排序方法
        insertionSort(arr);

        System.out.println("排序后的数组:");
        printArray(arr);
    }
}

 

 二分查找

它的核心思想是通过不断缩小查找范围,将查找时间复杂度从 O(n) 降低到 O(log n)

 public class BinarySearch {

    // 二分查找方法
    public static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;

        while (left <= right) {
            int mid = left + (right - left) / 2; // 计算中间位置
            if (arr[mid] == target) {
                return mid; // 找到目标值,返回索引
            } else if (arr[mid] < target) {
                left = mid + 1; // 目标值在右半部分
            } else {
                right = mid - 1; // 目标值在左半部分
            }
        }
        return -1; // 未找到目标值
    }

    // 主方法:测试二分查找
    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9, 11}; // 有序数组
        int target = 7; // 目标值

        int result = binarySearch(arr, target);
        if (result != -1) {
            System.out.println("目标值 " + target + " 的索引是: " + result);
        } else {
            System.out.println("目标值 " + target + " 未找到");
        }
    }
}

对数器

它的核心思想是通过对比待测试算法和一个已知正确的算法(通常是一个暴力解法或标准库函数),在大量随机测试数据上运行,确保两者结果一致。

master公式(适用与分治类型的)

T(N) = a*T(N/b) + O(N^d)

a表示每次递归产生的子问题数量

b表示相对原问题缩小的比例 

O(N^d) 表示原问题分解为子问题以及合并子问题解所需的工作量

归并排序

它的核心思想是将一个大问题分解成若干个小问题,分别解决这些小问题,然后将结果合并起来,最终得到问题的解。归并排序的时间复杂度为 O(n log n),是一种稳定的排序算法。

function mergeSort(arr):
    if length of arr <= 1:
        return arr  // 数组长度为 1 或 0,直接返回

    mid = length of arr / 2
    left = mergeSort(arr[0:mid])  // 对左半部分递归排序
    right = mergeSort(arr[mid:])  // 对右半部分递归排序

    return merge(left, right)  // 合并两个有序子数组

function merge(left, right):
    result = []  // 用于存储合并后的结果
    i = j = 0  // 初始化左右子数组的指针

    while i < length of left and j < length of right:
        if left[i] < right[j]:
            result.append(left[i])
            i++
        else:
            result.append(right[j])
            j++

    // 将剩余的元素加入结果数组
    while i < length of left:
        result.append(left[i])
        i++

    while j < length of right:
        result.append(right[j])
        j++

    return result

 快速排序

选择一个基准元素(pivot):通常选择数组的第一个元素、最后一个元素或中间元素。

分区(Partition):将数组分为两部分,使得:

  • 左边的元素都小于等于基准元素。
  • 右边的元素都大于基准元素。

递归排序:对左右两部分分别递归调用快速排序。

import java.util.Arrays;
public class QuickSort{

    public static void main(String[] args){
        int[] arr = {2,3,1,6,7,8,9};
        quickSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void quickSort(int[] arr){
        if(arr == null || arr.length <=1 ){
            return;
        }
        paiXu(arr,0,arr.length - 1);
    }

    private static void paiXu(int[] arr, int left, int right){
        if(left < right){
            int pivotIndex;
            pivotIndex= partition(arr,left,right);
            paiXu(arr,left,pivotIndex-1);
            paiXu(arr,pivotIndex+1,right);
        }
    }
    private static int partition(int[] arr,int left,int right){
        int pivot = arr[right];
        int i = left;
        for(int j = left;j < right;j++){
            if(arr[j] < pivot){
                swap(arr,i,j);
                i++;
            }
        }
        swap(arr,i,right);
        return i;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

 堆排序

package org.dty.sort;

public class heap {
    public static void heapSort(int[] arr) {
        int n = arr.length;

        // 1. 建堆
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }

        // 2. 排序
        for (int i = n - 1; i > 0; i--) {
            // 将堆顶元素(最大值)与堆的最后一个元素交换
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            // 调整堆,使其重新满足最大堆的性质
            heapify(arr, i, 0);
        }
    }

    // 调整堆
    private static void heapify(int[] arr, int n, int i) {
        int largest = i; // 假设当前节点是最大值
        int left = 2 * i + 1; // 左子节点
        int right = 2 * i + 2; // 右子节点

        // 如果左子节点比当前节点大
        if (left < n && arr[left] > arr[largest]) {
            largest = left;
        }

        // 如果右子节点比当前节点大
        if (right < n && arr[right] > arr[largest]) {
            largest = right;
        }

        // 如果最大值不是当前节点
        if (largest != i) {
            // 交换当前节点和最大值节点
            int temp = arr[i];
            arr[i] = arr[largest];
            arr[largest] = temp;

            // 递归调整受影响的子树
            heapify(arr, n, largest);
        }
    }


    public static void main(String[] args){
        int[] arr = {12, 11, 13, 5, 6, 7};
        heapSort(arr);
        System.out.println("排序后的数组:");
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}

 

posted @   丁同亚的博客  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示