0
点赞
收藏
分享

微信扫一扫

leetCode进阶算法题+解析(二十三)

今天和我一个宿舍的小姑娘离职了,搬家搬了一天。收拾东西打包来回倒腾。感觉自此一别,不出意外也就是一辈子了。倒也没多伤感,大概习惯了吧。

生活就像坐火车,
身边的人去去留留,停停走走
有人上车就会有人下车
有人坐着就会有人站着
安心旅途,安静生活

好了,开始刷题了!

对链表进行插入排序。

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

思路:这个图我也弄不过来,大概讲一下吧,是个升序排列,从第二个元素开始,挨个比对,按升序插入。然后第三个第四个逐次往下排序。然后排序的队列也越来越长,直到最后一个也插入到应该插入的为止既排好序了。如果这个是数组比较那么简直是伸手就来,但是这个题是链表。还好没什么时间空间复杂度的要求,我觉得也不是很难做。我目前的想法就是一个链表顺着往下挑节点,另一链表是链表头,然后从前往后选择插入点。具体怎么做我去写代码试试.
好了,做完出来了,因为这个链表来回来去插入和取出很麻烦,所以我注释写的很清楚,不断微调好久。。我直接贴代码了:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode first = new ListNode(0);
        first.next = head;
        //当head.next等于null说明没有需要插入排序的节点了
        while(head != null && head.next != null){
            if(head.val<= head.next.val){//已经是升序不用排了
                head = head.next;
            }else{
                ListNode temp = first;
                //从头结点开始,找到应该插入的位置(第一个比他大的节点的前一个)
                while(temp.next.val<head.next.val) temp = temp.next;
                ListNode cur = head.next;
                head.next = cur.next;
                //上两句的作用是head.next = head.next.next.把head的下一个跳过去了并存在了cur中
                cur.next = temp.next;
                temp.next = cur;
                //这两句是把 cur插在了 temp 和 temp.next之间  
            }
        }
        return first.next;
    }
}

然后这个性能超过了百分之九十八的人,一次及格,所以我也不多看了,直接下一题。

排序链表

题目:在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

思路:讲真,这块链表的题都不算太难啊。然后这个题排序是次要的,甚至上道题也排序了啊。重点是常量空间复杂度,和O(n)时间复杂度啊。logn是什么概念?我记得二分法好像就是吧?说到二分,用归并么?啧啧,这个题我先理理思路。看看归并能不能实现吧。
有点绝望,我怎么想也没相处来O(n)时间复杂度,哪怕归并也没用。。我还是先随缘实现下,然后看题解吧。
好了,用的归并完成的,我先贴代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        return mergeSort(head);
    }
    public ListNode mergeSort(ListNode head){
        if(head==null || head.next==null) return head;
        ListNode f = head;
        ListNode s = head;
        ListNode p = null;
        while(f != null && f.next != null){
            p = s;
            s = s.next;
            f = f.next.next;
        }
        //这一步是使得head 和 s 将一个完整的链表中间拆成两段
        p.next = null; 
        ListNode l = mergeSort(head);
        ListNode r = mergeSort(s);
        p = new ListNode(0);
        ListNode res = p;
        while(l != null && r != null){
            if(l.val<r.val) {
                p.next = l;
                p = p.next;
                l = l.next;
            } else {
                p.next = r;
                p = p.next;
                r = r.next;
            }
        }
        if(l != null){
            p.next = l;
        }
        if(r != null){
            p.next = r;
        }
        return res.next;
    }
}

完美的没用额外空间(说起来上一道插入的那个题也没用额外空间)至于时间复杂度就这样吧,我去看看性能第一的代码了。
额,有点失望。。。不是有点,是大大大大大的失望了,性能排行第一的是什么玩意,不仅时间复杂度不行,空间复杂度都没做到。。。我贴出来下,不用瞻仰:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null)
            return head;
        ListNode cur = head;
        int count = 0;
        //获取长度
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        int[] arr = new int[count];
        int index = 0;
        cur = head;
        //值导入数组
        while (cur != null) {
            arr[index++] = cur.val;
            cur = cur.next;
        }
        //排序
        Arrays.sort(arr);
        
        cur = head;
        index = 0;
        //重新赋值
        while (cur != null) {
            cur.val = arr[index++];
            cur = cur.next;
        }
        return head;
    }
}

然后接下来去看看题解了,找找别人的思路:
呵,千篇一律归并,总算有一个说不是归并还是c++写的。。。有点看不懂。。这个题可能就是这么无解的吧。。。我就过了。下一题了。

逆波兰表达式求值

题目:根据逆波兰表示法,求表达式的值。有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:整数除法只保留整数部分。给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

思路:这个题怎么说呢,我看了半天这个表达式,总算是有点懂的意思了。首先,一个数字后面紧跟着一个符号,说明是X 符号 数字 这么个表达式。 而X是未知数,具体是什么还要看前面如果是数字则是数字,如果是符号说明X也是一个表达式。反正就是这个意思。我感觉做法应该是有能进阶的就立刻进阶。最开始的那个符号对应的肯定是两个数字。。语言表达不清楚,我去实现试试
好了,做出来了,1110ms,性能吓人:

class Solution {    
    public int evalRPN(String[] tokens) {
        if(tokens.length == 0) return 0;
        List<String> list = new ArrayList<>(tokens.length);
        for (String s : tokens) {
            list.add(s);
        }
        dfs(list);
        return Integer.parseInt(list.get(0));
        
    }
    public void dfs(List<String> list){
        if(list.size()==1) return;
        int n = 0;
        int idx = 0;
        for(int i = 0;i<list.size();i++){
            if(list.get(i).equals("+")){
                int f = Integer.parseInt(list.get(i-2));
                int s = Integer.parseInt(list.get(i-1));
                n = f + s;
                idx = i;
                break;
            }
            if(list.get(i).equals("-")){
                int f = Integer.parseInt(list.get(i-2));
                int s = Integer.parseInt(list.get(i-1));
                n = f - s;
                idx = i;
                break;
            }
            if(list.get(i).equals("*")){
                int f = Integer.parseInt(list.get(i-2));
                int s = Integer.parseInt(list.get(i-1));
                n = f * s;
                idx = i;
                break;
            }
            if(list.get(i).equals("/")){
                int f = Integer.parseInt(list.get(i-2));
                int s = Integer.parseInt(list.get(i-1));
                n = f / s;
                idx = i;
                break; 
            }
        }
        list.set(idx,String.valueOf(n));
        list.remove(idx-1);
        list.remove(idx-2);        
        dfs(list);
    }
}

因为第一版本比较无脑,我主要是看思路是不是对的,应该是有别的办法。
自己优化半天性能也不能,最好也是290.所以我直接看大佬思路了。
我直接贴出来,是从后往前递归,我甚至觉得有点动规的意思了。。哎,还是思路不行,我先贴代码:

class Solution {
    public int evalRPN(String[] tokens) {
           index = tokens.length - 1;
        return getPrefix(tokens);
    }

     int index;

    public  int getPrefix(String[] tokens) {
        String token = tokens[index--];
        if (token.equals("+")) {
            int prefix1 = getPrefix(tokens);
            int prefix0 = getPrefix(tokens);
            return prefix0 + prefix1;
        } else if (token.equals("-")) {
            int prefix1 = getPrefix(tokens);
            int prefix0 = getPrefix(tokens);
            return prefix0 - prefix1;
        } else if (token.equals("*")) {
            int prefix1 = getPrefix(tokens);
            int prefix0 = getPrefix(tokens);
            return prefix0 * prefix1;
        } else if (token.equals("/")) {
            int prefix1 = getPrefix(tokens);
            int prefix0 = getPrefix(tokens);
            return prefix0 / prefix1;
        } else {
            return Integer.parseInt(token);
        }
    }
}

一看就会,一写就废系列。其实代码不是多难,但是我就没想到这么从后往前。而是傻傻的从前往后。并且为此用了list,来回来去删除重写。
总的来说今天的三道题比较简单,然后就这样了吧。
今天的笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。也祝大家工作顺顺利利,生活健健康康!周末愉快!

举报

相关推荐

0 条评论