【剑指offer第二版】JAVA刷题总结-ch3

16.数值的整数次方
思路:考虑特殊情况(1)底数为0;(2)指数为0;(3)指数为负数(3)指数

class Solution {
    public double Power(double base, int exponent) {
        double result;
        if(base==0) return 0;
        else if(exponent==0) return 1;
        else if(exponent<0){
            result = 1/powerCore(base, -exponent);
        }
        else result = powerCore(base, exponent);
        return result;
    }
    public double powerCore(double base, int exponent){
        if(exponent==0) return 1;
        if(exponent==1) return base;
        double result = powerCore(base, exponent>>1);
        if(exponent % 2 == 1)  result = result * result * base; 
        else result = result * result;
        return result;
    }
}

17. 打印从1到最大的n位数
思路:大数问题,全排列,利用递归来做

18. 删除链表的节点
18-1. O(1)时间内删除链表节点
思路:时间复杂度要求较高,我们只能修改链表中指针的值。处理两种特殊情况(1)头结点(2)尾结点:仍要按顺序查找

public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
    if (head == null || tobeDelete == null)
        return null;
    if (tobeDelete.next != null) {
        // 要删除的节点不是尾节点
        ListNode next = tobeDelete.next;
        tobeDelete.val = next.val;
        tobeDelete.next = next.next;
    } else {
        if (head == tobeDelete)
             // 只有一个节点
            head = null;
        else {
            ListNode cur = head;
            while (cur.next != tobeDelete)
                cur = cur.next;
            cur.next = null;
        }
    }
    return head;
}

18.2 删除链表中重复的节点
思路:#设置两个节点(1)pre指向重复节点段的前一个节点(2)now指向重复节点段的下一个节点,利用两个指针之间的距离,如果距离大于一,说明有重复段,如果距离不大于一,说明没有重复段。#注意,虚拟头结点dummy的使用可以使得无需考虑头节点被删除,但要记得利用构造函数赋初值。

class Solution {
    public ListNode deleteDuplication(ListNode head) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;//虚拟头结点,不需考虑删除头结点的问题。
        ListNode pre = dummy;
        while(pre!=null && pre.next!=null){
            ListNode now = pre.next;
            while(now!= null && pre.next.val == now.val) now = now.next;//now走一步,如果有重复,now走多步。now永远指向重复节点的下一个节点。
            if(pre.next.next == now) pre = pre.next;// 如果now只走了一步,说明这次没有重复,pre也前进一步
            else pre.next = now;//否则,pre不前进,pre的下一个就是now
        }
        return dummy.next;  
    }
}

19. 正则表达式匹配
思路:(1)动态规划:两个字符串是否匹配,多用动态规划,节省内存。子问题的解存起来。O(mn)(2)递归:正常递归

class Solution {
    enum RESULT{
        TRUE,FALSE;
    }//定义一个枚举类型而不用boolean,因为可以有null这种情况;boolean只有true,false两种,不能用来记录是否存储过。
    public RESULT[][] f;//f用来记录是否存储过,如果存储过那么s[i]...s[n]是否与p[j]...p[n]匹配
    public boolean isMatch(String s, String p) {
        f = new RESULT[s.length()+1][p.length()+1];
        return dp(0, 0, s, p);//dp
    }
    public boolean dp(int m, int n, String s, String p){
        int sSize = s.length();
        int pSize = p.length();
        boolean ans;//用来记录return 的值
        if(f[m][n]!=null) return f[m][n]==RESULT.TRUE;//访问过,直接返回存储的值。
        if(n == pSize){//如果p到了最后一位,s是否到最后一位,如果不到最后一位,说明有没匹配上的,return false
            ans = m==sSize;
            f[m][n] = ans?RESULT.TRUE:RESULT.FALSE;
            return ans;
        }
        boolean firstEqual = m<sSize && (s.charAt(m)==p.charAt(n) || p.charAt(n)=='.');//比较当前字符号是否相等(字符相等或者.)
        if(n+1<pSize && p.charAt(n+1)=='*'){//如果是下一个字符是*,考虑重复0次,重复>=1次
            ans = dp(m,n+2,s,p) || firstEqual&&dp(m+1,n,s,p);
        }
        else{
            ans = firstEqual && dp(m+1,n+1,s,p);//普通字符或者.
        }
        f[m][n] = ans?RESULT.TRUE:RESULT.FALSE;//记录是否相等,并且已经访问过
        return ans;//返回是否相等
    }
}

20.表示数值的字符串
思路:将数字的形式总结为:(A.B E/e A) ,按顺序进行判断(A代表带符号整数,B代表不带符号整数)

class Solution {
    public boolean isNumber(String s) {
        if(s.length()<=0 ) return false;
        int[] index = new int[1];//传引用,方便修改index
        index[0] = 0;
        boolean numeric = scanInteger(s, index);
        if(index[0]<s.length() && s.charAt(index[0])=='.'){
            index[0]++;
            numeric = scanUnsigned(s, index)||numeric;
        }
        if(index[0]<s.length() && (s.charAt(index[0])=='E' || s.charAt(index[0])=='e')){
            index[0]++;
            numeric = scanInteger(s, index)&& numeric;
        }
        if(index[0]==s.length()) return numeric;
        return false;
    }
    public boolean scanInteger(String s, int[] index){
        if(index[0] < s.length() && (s.charAt(index[0])=='+' || s.charAt(index[0])=='-'))
            index[0]++;
        return scanUnsigned(s, index);
    }
    public boolean scanUnsigned(String s, int[] index){
        int begin = index[0];
        while(index[0] < s.length()){
            if(s.charAt(index[0]) >= '0' && s.charAt(index[0]) <='9') index[0]++;
            else break;
        }
        if(begin<index[0]) return true;
        else return false;
    }
}

21. 调整数组顺序使奇数位于偶数前面
思路(1)前后两个index, 同时遍历,做交换。注意位运算优先级,==运算优先级高于&,记得加括号。(2)

class Solution {
    public void reOrderArray(int [] array) {
        int begin = 0;
        int end = array.length-1;
        while(begin < end ){
            if((array[begin]&1)==1 && (array[end]&1)==0){
                begin++;
                end--;
            }
            else if((array[begin]&1)==1 && (array[end]&1)==1){
                begin++;
            }
            else if((array[begin]&1)==0 && (array[end]&1)==0){
                end--;
            }
            else{
                int tmp = array[begin];
                array[begin] = array[end];
                array[end] = tmp;
                begin++;
                end--;
            }
        }
    }
}

22. 链表中倒数第k个节点
思路:要求只遍历链表一次,两个指针,before,after;after先走k-1步,两个指针再一起走,当after最后一个,before就是要返回的倒数第k个。注意特殊情况,k<链表长度;链表为空。

class Solution {
    public ListNode findKthToTail(ListNode pListHead, int k) {
        ListNode before = pListHead;
        ListNode after = pListHead;
        if(pListHead == null) return null;
        for(int i=0; i<k-1; i++){
            if(after.next != null) after = after.next;
            else return null;
        }
        while(after.next != null){
            after = after.next;
            before = before.next;
        }
        return before;
    }
}

23.链表中环的入口节点
思路:(1)先判断是否有环,有环返回相遇点;相遇点再次走到相遇点的距离,即为环的长度;p1,p2,p2先走出环的长度,p1p2再一起走,相遇点,即为环的入口节点(2)巧妙思路:p2走两步,p1走一步,记录相遇的点。p2回到起点,p1从相遇点出发,步速都为1,再次相遇的点即为环的入口。证明如下:
在这里插入图片描述在这里插入图片描述

class Solution {
    public ListNode entryNodeOfLoop(ListNode head) {
        ListNode meetingNode = isCircle(head);
        if(meetingNode == null) return null;
        int count = 1;
        ListNode cur = meetingNode;
        while(cur.next!=meetingNode){
            count++;
            cur = cur.next;
        }
        ListNode before = head; 
        ListNode after = head;
        for(int i=0; i<count;i++){
            after = after.next;
        }
        while(after!=before){
            after = after.next;
            before = before.next;
        }
        return before;
    }
    public ListNode isCircle(ListNode head){
        if(head==null) return null;
        ListNode slow = head.next;
        if(slow==null) return null;
        ListNode fast = slow.next;
        while(fast!=null){
            if(fast==slow) return slow;
            slow = slow.next;
            fast = fast.next;
            if(fast!=null) fast = fast.next;
        }
        return null;
    }
}
class Solution {
    public ListNode entryNodeOfLoop(ListNode head) {
        if (head == null || head.next == null)
            return null;
        ListNode slow = head.next;
        if(slow==null) return null;
        ListNode fast = slow.next;
        while (slow != fast){
            if(fast!=null){
                fast = fast.next.next;
                slow = slow.next;
            }
            else return null;
        }
        fast = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

24.翻转链表
思路:虚拟头结点

    public ListNode reverseList(ListNode head) {
        if(head==null) return null;
        ListNode dummy = new ListNode(-1);
        dummy.next = null;
        while(head!=null){
            ListNode tmp = dummy.next;
            dummy.next = head;
            head = head.next;
            dummy.next.next = tmp;
        }
        return dummy.next;
    }

25.合并两个排序的链表
思路:设置虚拟头结点,并判断特殊情况。

    public ListNode merge(ListNode l1, ListNode l2) {
        if(l1==null && l2==null) return null;
        if(l1==null) return l2;
        if(l2==null) return l1;
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                cur.next = l1;
                l1 = l1.next;
                cur = cur.next;
            }
            else{
                cur.next = l2;
                l2 = l2.next;
                cur = cur.next;
            }
        }
        if(l1!=null) cur.next=l1;
        if(l2!=null) cur.next=l2;
        return dummy.next;
    }

26. 树的子结构:
思路:用递归解决。先判断以当前节点为根节点的树A是否包含B;如果不包含,判断左节点有没有子树,包不包含B;否则,判断右子树。

class Solution {
    public boolean hasSubtree(TreeNode pRoot1, TreeNode pRoot2) {
        boolean result = false;
        if(pRoot1!=null && pRoot2!=null){//根节点不为空
            if(Math.abs(pRoot1.val-pRoot2.val)<1e-6)//根节点相等
                result = isAHaveB(pRoot1, pRoot2);//如果A包含B,返回result;
            if(result != true)//如果不包含,去查左节点
                result = hasSubtree(pRoot1.left, pRoot2);
            if(result != true)//左节点还不包含,去查右节点
                result = hasSubtree(pRoot1.right, pRoot2);
        }
        return result;
    }
    public boolean isAHaveB(TreeNode p1, TreeNode p2){
        if(p2==null) return true;
        if(p1==null) return false;
        if(Math.abs(p1.val-p2.val)<1e-6) 
            return (isAHaveB(p1.left,p2.left) && isAHaveB(p1.right, p2.right));
        return false;
    }
}
发布了16 篇原创文章 · 获赞 0 · 访问量 3122

猜你喜欢

转载自blog.csdn.net/Calliope1997/article/details/104203480