一般的に使用されるアルゴリズムの個人的な要約(ソート、2の累乗、二分法など)

目次

1.二分探索

2.バックトラッキングアルゴリズムの基本的な考え方:

3.グラフの深さと幅の走査に優先順位を付けます。

4.高速電力

5.一般的に使用されるソートアルゴリズム

6.二分木をトラバースする方法:

7。

8.8。

9.9。

 


1.二分探索

    /**
     * 二分查找,找到该值在数组中的下标,否则为-1
     */
    public static int binarySerach(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;
        while (left <= right) {                  // 这里必须是 <=
            int mid = left + (right - left) / 2;      // 养成习惯 防止溢出
            if (array[mid] == key) {
                return mid;
            } else if (array[mid] < key) {          //这里是小于号,可以加上等于(加上等于左边界不断递增,就是查找最后一个等于key的元素)
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }

2.バックトラッキングアルゴリズムの基本的な考え方:

// 重复元素可以用if(i>0 &&nums[i]==nums[i-1]&&!used[i-1])进行剪枝
    public void dfs(int k, int[] nums) {
        if (k == nums.length) {
            res.add(new ArrayList<>(tmp));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (used[i])
                continue;
            tmp.add(nums[i]);           //可用tmp.set(index,value)代替,后面就不应remove
            used[i] = true;
            dfs(k + 1, nums);
            used[i] = false;
            tmp.remove(tmp.size()-1);    //回溯 还原状态
        }
    }

3.グラフの深さと幅が最初にトラバースされます。

//DFS
    {
        int level = 0;
        while (!deque.isEmpty()) {
            size = deque.size();
            while (size-- > 0) {
                cur = deque.pop();
                foreach 节点 in cur所有的相邻节点:
                if 该节点不为空且没有被访问过:
                deque.offer(该节点)
            }
            level++;
        }
    }
    // BFS
    //类似回溯
    DFS(int deep,...){
        if (找打解||走不下去了){
            ...
            return;
        }
        DFS(int deep+1,....)  // 枚举下一种情况
    }

4.高速電力

   //迭代版
    int qpow(int a, int n) {
        int ans = 1;
        while (n > 0) {
            if ((n & 1) == 1)        //如果n的当前末位为1
                ans *= a;  //ans乘上当前的a
            a *= a;        //a自乘
            n >>= 1;       //n往右移一位
        }
        return ans;
    }
    //递归版
    int qpow(int a, int n) {
        if (n == 0) return 1;
        if ((n & 1) == 1)
            return a * qpow(a, n - 1);
        else {
            int tmp = qpow(a, n / 2);    //奇数会自动转为int
            return tmp * tmp;
        }
    }

5.一般的に使用されるソートアルゴリズム

public static void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

    public static void BubbleSort(int[] nums) {
        for (int i = 0; i < nums.length - 1; i++) {
            //boolean flag = true; //优化 如果一趟没有做过交换,就直接结束。
            for (int j = 0; j < nums.length - i - 1; j++) {
                if (nums[j] > nums[j + 1]) {
                    swap(nums, j, j + 1);
                    // flag = false;
                }
            }
            //if (flag) break;
        }
    }

    public static void SelectSort(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            int minIndex = i;               //最小的数对应的下标
            for (int j = i; j < nums.length; j++) {
                if (nums[j] < nums[minIndex])
                    minIndex = j;
            }
            swap(nums, i, minIndex);
        }
    }

    public static void InsertSort(int[] nums) {
        for (int i = 1; i < nums.length; i++) { //从第2个数据开始插入
            int temp = nums[i];                  //记录要插入的数据
            int j = i - 1;
            while (j >= 0 && nums[j] > temp) {   //从后向前移动,比他大的都向后移
                nums[j + 1] = nums[j];
                j--;
            }
            nums[j + 1] = temp;
        }
    }


    public static void BuildMaxHeap(int[] nums) {//构建一个大顶堆,从最下面的非叶子节点开始向上遍历
        for (int i = nums.length / 2 - 1; i >= 0; i--) {    //注意,下标从0开始,i的子节点是2i+1,2i+2,反之
            adjustHeap(nums, i, nums.length);
        }
    }

    public static void adjustHeap(int[] arr, int parent, int length) {
        int temp = arr[parent];          //临时保存父节点
        int child = 2 * parent + 1;     //左子节点的下标
        while (child < length) {
            if (child + 1 < length && arr[child] < arr[child + 1]) {  //判断左子节点和右子节点的大小,若右边大,则把child定位到右边
                child++;
            }
            if (arr[child] > temp) {      //若child大于父节点,则交换位置,交换位置之后,不能保证当前的子节点是它子树的最大值,所以需要继续向下比较,把当前子节点设置为下次循环的父节点,同时,找到它的左子节点,继续下次循环
                arr[parent] = arr[child];
                parent = child;
                child = 2 * parent + 1;
            } else {                      //如果当前子节点小于等于父节点,则说明此时的父节点已经是最大值了,结束循环
                break;
            }
        }
        arr[parent] = temp;  //把当前节点(parent可能已经改变)替换为最开始暂存的父节点值
    }

    public static void MergeSort(int[] nums, int low, int high) {
        if (low >= high)
            return;
        int mid = low + (high - low) / 2;
        MergeSort(nums, low, mid);          // 递归排序前半部分
        MergeSort(nums, mid + 1, high);  // 递归排序后半部分
        merge(nums, low, mid, high);        // 归并
    }

    public static void merge(int[] nums, int low, int mid, int high) {
        int i = low, j = mid + 1;                                 //low为左半部分的起始inex
        int[] temp = Arrays.copyOf(nums, nums.length);     //复制一份
        for (int index = low; index <= high; index++) {
            if (i > mid)                                  //左半部分i<low<mid,每次执行完i++,(mid-1完成后i=mid已经越界了)因此这里不能等于
                nums[index] = temp[j++];
            else if (j > high)                             //有半部分 mid+1 <j<high同理,
                nums[index] = temp[i++];
            else if (temp[i] < temp[j])
                nums[index] = temp[i++];
            else
                nums[index] = temp[j++];
        }
    }

    public static void QuickSort(int[] nums, int low, int high) {
        if (low >= high)
            return;
        int index = partion(nums, low, high);
        QuickSort(nums, low, index - 1);
        QuickSort(nums, index + 1, high);
    }

    public static int partion(int[] nums, int low, int high) {
        int pivot = nums[low];
        while (low < high) {
            while (low < high && nums[high] >= pivot) high--;
            nums[low] = nums[high];
            while (low < high && nums[low] <= pivot) low++; //从左往右扫描,找到第一个比基准值大的元素
            nums[high] = nums[low];
        }
        nums[low] = pivot;
        return low;
    }

6.二分木をトラバースする方法:

    // 先序遍历,根左右,遍历把根push进stack,然后push 右子树,左子树。由于根在(根左右)前面,每次子遍历根都在前面,压进去就出来了,可忽略根的属性。由于栈的特性。左右子树压进去的顺序相反
    public static void preOrderRecur(TreeNode head) {
        if (head == null) return;
        System.out.print(head.value + " ");//res.add(head.val) 访问
        preOrderRecur(head.left);
        preOrderRecur(head.right);
    }
    //--------------------------------------------------------
    public void preorderTraversal(TreeNode p) {
        if (p == null) return;
        Deque<TreeNode> stack = new ArrayDeque<>();
        stack.push(p);
        while (!stack.isEmpty()) {
            TreeNode tmp = stack.pop();     // poll()不会抛异常
            System.out.print(tmp.val + " ");//res.add(tmp.val);
            if (tmp.right != null) stack.push(tmp.right);
            if (tmp.left != null) stack.push(tmp.left);
        }
    }

    // 中序遍历,每次要一直向左子树压到底直到不能在压入,每次弹出一个加入结果集,如果有右子树,就把右子树当做当前节点,重复上述操作。
    public static void inOrderIteration(TreeNode p) {
        if (p == null) return;
        Deque<TreeNode> stack = new LinkedList<>();
        while (!stack.isEmpty() || p != null) {           //栈不空,当前不空
            while (p != null) {                           //左孩子非空,一直压栈
                stack.push(p);
                System.out.println(":push:" + p.val);
                p = p.left;
            }
            p = stack.pop();                          //弹出加到结果集合中,赋给右孩子,重复上一个while 一直压栈
            res.add(p.val);//print
            p = p.right;
        }
    }

    // 后续遍历的第一种方式
    // 后续遍历,是左右根的操作。倒过来就是根左右(利用栈的特性打的过来)。有没有很熟悉,前序是根右左,因此压栈顺序相反就行(前序是先压右子树,因此这里是先压左子树)
    public static void postOrderIteration(TreeNode root) {
        if (root == null) return res;
        Deque<TreeNode> stack = new ArrayDeque<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode tmp = stack.pop();
            res.add(0, tmp.val); //相当于头插法实现一次reverse//print
            if (tmp.left != null)
                stack.push(tmp.left);
            if (tmp.right != null)
                stack.push(tmp.right);
        }
    }

    // 后续遍历的第二种方式
    // 左右根 遍历的时候 第一次碰到根节点(任意的子过程中的根节点,可以视为任意节点)压栈,从左返回不管,从右返回弹出节点。
    // 左右根 同中序 左根右 , 前面两个while 到底  ,看看栈顶的右边值是不是空,不为空看看是不是第一次走,第一次走把他压栈,不然就访问,记得置p值,一个是left一个是null
    public static void PostOrderIteration2(TreeNode p) {
        if (p == null) return;
        TreeNode last = p;
        Deque<TreeNode> stack = new LinkedList<>();
        while (!stack.isEmpty() || p != null) {
            while (p != null) {  //一直压入直到左孩子为空
                stack.push(p);
                p = p.left;
            }
            p = stack.peek().right;                  //注意此时p的左孩子为空或者已经访问过
            if (p != null && p != last) {            //如果右孩子不为空且右孩子不是上一次访问过的节点  注意这里是左右孩子 不是p
                stack.push(p);
                p = p.left; //回归下一次循环
            } else {                                //左右孩子皆为空
                p = stack.pop();                   //弹出
                res.add(p.val);                      //访问一次加到结果中
                last = p;
                p = null;                           //把p置为空,为了弹栈,如果不置空,将重复压栈
            }
        }
    }

    // 层次遍历,队列加入root, while栈不空,while (size -- >0)
    public List<List<Integer>> levelOrder(TreeNode root) {
        if (root == null) return new ArrayList<>();
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(root);
        while (!que.isEmpty()) {
            List<Integer> tmp = new ArrayList<>();
            // 关键之处,仔细体会
            int size = que.size();
            while (size-- > 0) {
                TreeNode node = que.poll();
                tmp.add(node.val);
                if (node.left != null)
                    que.offer(node.left);
                if (node.right != null)
                    que.offer(node.right);
            }
            res.add(tmp);
        }
        return res;
    }

 

7。

8.8。

 

おすすめ

転載: blog.csdn.net/yu1336199790/article/details/114808484