2020-11-05

21 篇文章 0 订阅

11/5
今天看的有jvm内容,回溯算法,还有排序
先总结排序,今天做的是插入排序和选择排序,至于冒泡排序就不写了。
插入排序做的是LeetCode的题目:
147. 对链表进行插入排序

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insertion-sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
插入排序算法:

插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insertion-sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码思路:跟普通的插入排序思想一样,在循环到的元素下标之前找出比这个元素更大的元素,把这两个元素进行交换,注意这个交换是在把前面排序好的元素一一遍历交换的!直至遍历到第一个元素。
在写这道代码题之前,先写下普通数组的插入排序:

public class Solution {


    public int[] sortArray(int[] nums) {

        int len = nums.length;

        for (int i = 1; i < len; i++) {
			//遍历之前的全部元素!
            for (int j = i; j > 0; j--) {

                // 注意:前面的数严格大于后面的数才交换

                if (nums[j - 1] > nums[j]) {

                    swap(nums, j, j - 1);

                } else {

                    break;

                }

            }

        }

        return nums;

    }


    private void swap(int[] arr, int index1, int index2) {

        int temp = arr[index1];

        arr[index1] = arr[index2];

        arr[index2] = temp;

    }

}

然后进行这道题的分析,首先这道题是一个单向链表,所以第一个问题是如何进行前面节点的遍历,这里首先要把头结点变成普通节点,所以先进行了创建一个新的“头结点”,然后这个新的头结点在每一次循环之后都回归到头结点位置,这样我们就可以在每一次循环都从头结点到下一个循环的节点,然后再创建两个检查指针节点,一个表示当前循环到哪个节点,一个表示当前循环的下一个节点。
代码如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode insertionSortList(ListNode head) {
        if (head==null ||head.next==null)
            return head;
        ListNode flag=new ListNode(-1);
        flag.next=head;//创建新的头结点
        ListNode pre=head;//当前循环到的节点
        ListNode cur=head.next;//循环到的下一个节点,进行比较
        while(cur!=null){//遍历到最后的一个节点
            if(pre.val<cur.val){//如果这两个节点是升序的,就无须再进行排序了,直接进行下一个节点的遍历
                pre=pre.next;
                cur=cur.next;
            }else{//当下一个节点的数值比当前节点要小的时候,进行排序
                ListNode p=flag;//重置头结点
                while(p.next!=null&&p.next.val<cur.val){
                    p=p.next;//从头结点遍历到当前的节点,寻找比cur要大的节点
                }
                //下面四条语句是我个人觉得比较难理解的,举个例子说明假设链表为134253,cur.val=2
                pre.next=cur.next;//把cur节点先从链表中断开出来,链表现在为13453,2单独出来
                cur.next=p.next;//把遍历到的比cur大的节点放在原有cur的位置,链表现在为13453,2单独出来
                p.next=cur; //跟上一步对应,将这两个节点交换,链表现在为123453
                cur=pre.next;//进行下一节点的循环,cur.val现在为5
            }

        }
        return flag.next;
        }
    }
  1. 排序数组
    给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
    public int[] sortArray(int[] nums) {
        for(int i=0;i<nums.length-1;i++){
            int minindex=i;
            for(int j=i+1;j<nums.length;j++){
                if(nums[j]<nums[minindex])
                minindex=j;
            }
            swap(nums,i,minindex);
        }
        return nums;
    }
    public void swap(int[] nums,int index1,int index2){
        int temp=nums[index1];
        nums[index1]=nums[index2];
        nums[index2]=temp;
    }
}

没什么好说的,保存这道题,学完所有排序都试一下。

然后是回溯的大boss,今天是DFS的入门基础
第一道题
46. 全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

思路就是列出树结构,用dfs来进行,中间有回溯,具体代码解释和思路liweiwei1419大佬写的很明白,我就不多赘述了。
代码如下:

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        int len= nums.length;
        boolean[] userd=new boolean[len];
        var res=new ArrayList<List<Integer>>();
        if (len==0){
            return res;
        }
        List<Integer> path = new ArrayList<>();
        dfs(nums,len,0, path,userd,res);
        return res;
    }

    private void dfs(int[] nums, int len, int depth,
                     List<Integer> path, boolean[] used,
                     List<List<Integer>> res) {
        if (depth==len){
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < len; i++) {
            if (!used[i]) {
                path.add(nums[i]);
                used[i] = true;
                dfs(nums, len, depth + 1, path, used, res);
                used[i] = false;
                path.remove(path.size()-1);
            }
        }

    }
}

这是我写的第一道dfs算法题,首先是要写出树结构的所有可能,然后再分析其中的状态变量,这种算法真的巧妙啊。。。
然后第二道题跟上一道差不多,限定了输入可以重复,输出不能重复。
47. 全排列 II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

我一开始看到,心里想,这不是在判断里面加一个contains方法就可以了吗?!然后我也试了,确实可以,但是差点超时,绝了。
然后这道题的正确解法姿势是剪枝,具体操作也是看那位大佬
代码如下:

class Solution {

    public List<List<Integer>> permuteUnique(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }

        // 排序(升序或者降序都可以),排序是剪枝的前提
        Arrays.sort(nums);

        boolean[] used = new boolean[len];
        // 使用 Deque 是 Java 官方 Stack 类的建议
        Deque<Integer> path = new ArrayDeque<>(len);
        dfs(nums, len, 0, used, path, res);
        return res;
    }

    private void dfs(int[] nums, int len, int depth, boolean[] used, Deque<Integer> path, List<List<Integer>> res) {
        if (depth == len) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < len; ++i) {
            if (used[i]) {
                continue;
            }

            // 剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义
            // 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                continue;
            }

            path.addLast(nums[i]);
            used[i] = true;

            dfs(nums, len, depth + 1, used, path, res);
            // 回溯部分的代码,和 dfs 之前的代码是对称的
            used[i] = false;
            path.removeLast();
        }
    }
}

今天的算法题营养很丰富,希望能够牢记。然后jvm内容的话,今天其实看得也不多,感觉没到记下的程度,所以说今天还是摸鱼了?!但是算法整的我有点头大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值