剑指offer 2021/8/1

43 篇文章 0 订阅
27 篇文章 0 订阅

58 - I. 翻转单词顺序

法一:

class Solution {
    public String reverseWords(String s) {
        if(s.equals(""))
            return "";
        int m = 0;
        //去掉字符串前的空格
        while(s.charAt(m)==' '){
            s = s.substring(m+1);
            if(s.equals(""))
                return "";
        }
        //去掉字符串后的空格
        int n = s.length() - 1;
        while(s.charAt(n)==' '){
            s=s.substring(0,n);
            n--;
        }
        //将字符串以空格划分
        String[] split = s.split(" ");
        String res = "";
        for(int i = split.length - 1; i >= 0; --i){
            if(split[i].equals("")){
                continue;
            }
            if(i == 0){
                res += split[0];
            }else{
                res += split[i];
                res += ' ';
            }
            System.out.println(i+split[i]);
        }
        return res;
    }
}

法二:

和方法一思路一样,但是使用了trim方法来删除首尾空格。

class Solution {
    public String reverseWords(String s) {
        String[] split = s.split(" ");
        StringBuilder str = new StringBuilder();
        for(int i = split.length - 1; i >= 0; --i){
            if(split[i].equals("")){
                continue;
            }
            str.append(split[i] + " ");
        }
        return str.toString().trim();
    }
}

法三:

双指针法

class Solution {
    public String reverseWords(String s) {
        StringBuffer str = new StringBuffer();
        //i,j两个指针分别指向单词的首尾
        int i = s.length()-1, j = s.length()-1;
        while(i >= 0){
            //寻找单词的左边界
            while(i >= 0 && s.charAt(i) != ' ')   i--;
            str.append(s.substring(i+1, j+1) + " ");
            //寻找单词的有边界
            while(i >= 0 && s.charAt(i) == ' ')   i--;
            j = i;
        }
        //去掉首尾空格
        return str.toString().trim();
    }
}

注意:

每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象。
所以当程序中需要大量的对某个字符串进行操作时,应该考虑应用StringBuilder类处理该字符串,其设计目的就是针对大量string操作的一种改进办法,避免产生太多的临时对象;而当程序中只是对某个字符串进行一次或几次操作时,采用string类即可。

32 - III. 从上到下打印二叉树 III

法一:

双端队列。
设置一个flag标记,通过判断flag的值决定从左到右or从右到左输出。要注意插入和删除元素的位置。(可以不设置flag,因为在奇数层遍历完之后队列中已经有了偶数层的节点,只需判断队列是否为空,继续操作即可。)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        LinkedList<TreeNode> queue = new LinkedList<>();
        LinkedList<List<Integer>> res = new LinkedList<>();
        if(root == null)
            return res;
        queue.push(root);
        int flag = 0;
        while(!queue.isEmpty()){
            //保存每层的遍历结果
            LinkedList<Integer> temp = new LinkedList<>();
            int n = queue.size();
            if(flag == 0){
                for(int i = 0; i < n; ++i){
                    TreeNode node = queue.removeLast();
                    temp.add(node.val);
                    if(node.left!=null){
                        queue.push(node.left);
                    }
                    if(node.right!=null){
                        queue.push(node.right);
                    }
                }
            }else{
               for(int i = 0; i < n; ++i){
                    TreeNode node = queue.pop();
                    temp.add(node.val);
                    if(node.right!=null){
                        queue.add(node.right);
                    }
                    if(node.left!=null){
                        queue.add(node.left);
                    }
                } 
            }
            flag = 1-flag;
            res.add(temp);
        }
        return res;
    }
}

法二:

和方法一的思想类似,但是不用在插入删除节点的时候改变顺序,而是在将节点的val值插入temp列表时改变顺序。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        LinkedList<TreeNode> queue = new LinkedList<>();
        LinkedList<List<Integer>> res = new LinkedList<>();
        if(root != null)
            queue.add(root);
        while(!queue.isEmpty()){
            //保存每层的遍历结果
            LinkedList<Integer> temp = new LinkedList<>();
            for(int i = queue.size(); i > 0; --i){
                TreeNode node = queue.pop();
                //奇数层,从尾部插入
                if(res.size() % 2 == 0) temp.add(node.val);
                //偶数层,从前端插入
                else    temp.push(node.val);
                if(node.left != null)
                    queue.add(node.left);
                if(node.right != null)
                    queue.add(node.right);
            }
            res.add(temp);
        }
        return res;
    }
}

法三:

按普通层次遍历的方法进行遍历,最后判断若为奇数层,则将tmp数组中的元素反转。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        LinkedList<TreeNode> queue = new LinkedList<>();
        LinkedList<List<Integer>> res = new LinkedList<>();
        if(root != null)
            queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> tmp = new LinkedList<>();
            for(int i = queue.size(); i > 0; --i){
                TreeNode node = queue.pop();
                tmp.add(node.val);
                if(node.left!=null){
                    queue.add(node.left);
                }
                if(node.right!=null){
                    queue.add(node.right);
                }
            }
            //如果是偶数层,则将tmp反转
            if(res.size()%2 != 0)
                Collections.reverse(tmp);
            res.add(tmp);
        }
        return res;
    }
}

35. 复杂链表的复制

法一:哈希表

首次遍历将旧节点和新节点对应存在哈希表中,再次遍历给next和random赋值。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        Node cur = head;
        HashMap<Node, Node> map = new HashMap<>();
        while(cur != null){
            map.put(cur, new Node(cur.val));
            cur = cur.next;
        }
        cur = head;
        while(cur != null){
            //get(cur)是旧链表中cur节点在新链表中对应的节点
            //get(cur.next)是旧链表中cur节点的next节点在新链表中对应的节点
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return map.get(head);
    }
}

法二:

首先拼接新旧链表,接着循环给random赋值,最后拆分新旧链表,返回新链表头节点。

class Solution {
    public Node copyRandomList(Node head) {
        if(head==null){
            return null;
        }
        //将新旧链表合并为同一个
        Node cur = head;
        while(cur != null){
            Node tmp = new Node(cur.val);
            tmp.next = cur.next;
            cur.next = tmp;
            cur = tmp.next;
        }
        //给random赋值
        cur = head;
        while(cur != null){
            if(cur.random != null)
                cur.next.random = cur.random.next;
            cur = cur.next.next;
        }
        cur = head.next;
        Node res = head.next, pre = head;
        //拆分新旧链表
        while(cur.next != null){
            pre.next = pre.next.next;
            cur.next = cur.next.next;
            pre = pre.next;
            cur = cur.next;
        }
        pre.next = null;
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值