《剑指offer》刷题笔记(3)

文章目录

  • 二叉树的深度
  • 判断是否是平衡二叉树
  • 数组中只出现一次数字
  • 和为S的两个数字
  • 和为S的连续正数序列
  • 翻转单词顺序序列
  • 左旋转字符串
  • 找到环形链表的环入口
  • 5张扑克牌是否是连续的
  • 约瑟夫环
  • 求1 + 2 + ... + n
  • 不用加减乘除做加法
  • 把字符串转换为数字
  • 数组中重复的数字
  • 把二叉树打印成多行
  • 字符串流中第一个不重复字符
  • 之字型打印二叉树
  • 股票的最大利润(数组中两数差最大值)
  • 数据流中的中位数
  • 二叉搜索树的第k个结点
  • 滑动窗口最大值

二叉树的深度

非递归层次遍历

import java.util.Queue;
import java.util.LinkedList;
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null)
            return 0;
        Queue<TreeNode> queue = new LinkedList();
        int cur = 0, width = 0;	     //cur表明现在遍历到本层的第几个节点	
        int depth = 0;
        queue.offer(root);
        while(!queue.isEmpty())
        {
            width = queue.size();	//获取本层的宽度
            cur = 0;				//进入新一层,cur=0
            while(cur < width)
            {
                TreeNode node = queue.poll();
                if (node.left != null)
                    queue.offer(node.left);
                if (node.right != null)
                    queue.offer(node.right);
                cur++;
            }
            depth++;
        }
        return depth;
    }
}

递归写法

public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null)
            return 0;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left,right) + 1;
    }
}

判断是否是平衡二叉树

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if (root == null)
            return true;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        if(Math.abs(right - left) >=2)
            return false;
        return true;
    }
    public int TreeDepth(TreeNode root)
    {
        if(root == null)
            return 0;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left,right) + 1;
    }
}

数组中只出现一次数字

一般的思路是使用HashMap,初次之外的方法是使用异或的技巧

异或技巧的思路要点:

  • 用0逐个异或一个数组,数组中相同的数字会被抵消,最后i的结果相当于不重复的数组异或
  • 对数组所有数异或后结果进行分析,判断出从后往前第一个为1的位置,以该位置对所有数进行分类,这样得到的两个数仅仅只包括一个不重复的数,随后分别对这两个数进行0异或
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if (array == null || array.length<2)
            return;
        int n = 0;
        for (int i = 0;i < array.length; i++)
        {
            n ^=array[i];
        }
        int index = findBit1(n);
        num1[0] = 0;
        for(int i = 0;i <array.length;i++)
        {
            if(isBit1(array[i],index))
            {
                num1[0] ^= array[i];
            }
            else
                num2[0] ^= array[i];
        }
    }
    public int findBit1(int n)
    {
        int index = 0;
        while ((n & 1) == 0 && index <= Integer.SIZE)
        {
            n >>= 1;
            index++;
        }
        return index;
    }
    public boolean isBit1(int n, int index)
    {
        while (index != 0)
        {
            n >>= 1;
            index--;
        }
        return ((n&1) == 1);
    }
}

和为S的两个数字

数组为递增排序

法1:HashMap

import java.util.ArrayList;
import java.util.HashMap;

public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if (array == null || array.length < 1)
            return arrayList;
        HashMap<Integer,Integer> map = new HashMap<>();
        int[] temp = new int[2];
        int min = Integer.MAX_VALUE;
        for(int i:array)
        {
            if(!map.containsKey(sum - i))
                map.put(i,sum-i);
            else
            {
                if(i * (sum-1)<min)
                {
                    arrayList.clear();
                    arrayList.add(sum - i);
                    arrayList.add(i);
                }
            }
        }
        return arrayList;
    }
}

法2:双指针

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<>();
        if (array == null || array.length<1)
            return list;
        int l = 0;
        int r = array.length - 1;
        while (l <= r)
        {
            int temp = array[l] + array[r];
            if (temp == sum)
            {
                list.add(array[l]);
                list.add(array[r]);
                break;
            }
            if (temp < sum)
                l++;
            if (temp > sum)
                r--;
        }
        return list;
    }
}

和为S的连续正数序列

双指针的技巧

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> allList = new ArrayList<>();
        int l = 1;
        int r = 2;
        while (l < r)
        {
            int cur = getSum(l,r);
            if (cur == sum)
            {
                ArrayList<Integer> list = new ArrayList<>();
                for(int i = l; i <= r;i++)
                    list.add(i);
                allList.add(list);
                r++;
            }
            else if(cur > sum)
            {
                l++;
            }
            else if(cur < sum)
            {
                r++;
            }
        }
        return allList;
    }

    public int getSum(int m, int n)
    {
        int sum = 0;
        for (int i = m; i <= n;i++)
        {
            sum += i;
        }
        return sum;
    }
}

翻转单词顺序序列

先整体翻转,再翻转每一个单词,需要注意的是如果输入多个空格的情况,此时可以使用trim()的技巧

比较暴力的做法是从后往前一个一个处理

public class Solution {
    public String ReverseSentence(String str) {
        if (str.trim().equals(""))	//检查是否是全有空格组成
            return str;
        String temp = reverse(str);
        String [] strr = temp.split(" ");
        for(int i = 0;i < strr.length;i++)
        {
            strr[i] = reverse(strr[i]);
        }
        StringBuilder sb = new StringBuilder();
        for(int i = 0;i < strr.length;i++)
        {
            sb.append(strr[i] + " ");
        }
        return sb.toString().trim(); //trim()去掉多余的空格
    }
    public String reverse(String str)
    {
        StringBuilder sb = new StringBuilder(str);
        return sb.reverse().toString();
    }
}

左旋转字符串

先翻转要左移的字符串,再翻转整体字符串

public class Solution {
    public String LeftRotateString(String str,int n) {
        if (str.trim() == "")
            return str;
        if (n > str.length())
            return "";
        StringBuilder sb = new StringBuilder(str);
        StringBuilder sb1 = new StringBuilder(sb.substring(0,n));
        StringBuilder sb2 = new StringBuilder(sb.substring(n));
        sb.replace(0,n,sb1.reverse().toString());
        sb.replace(n,sb.length(),sb2.reverse().toString());
        return sb.reverse().toString();
    }
}

暴力做法

public class Solution {
    public String LeftRotateString(String str,int n) {
        if (str == null || str.length() < 1)
            return "";
        StringBuilder temp = new StringBuilder();
        for (int i = 0; i < n; i++)
        {
            temp.append(str.charAt(i));
        }
        StringBuilder sb = new StringBuilder(str);
        sb.delete(0,n);
        for (int i = 0; i < temp.length(); i++)
        {
            sb.append(temp.charAt(i));
        }
        return sb.toString();
    }
}

找到环形链表的环入口

先定义快慢两个指针,找到两个指针的相遇点,随后将一指针指向头结点,两个指针同时向后移动,再次相遇的结点为环入口

public class Solution {
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if (pHead == null || pHead.next == null || pHead.next.next == null)
            return null;
        ListNode p1 = pHead.next;
        ListNode p2 = pHead.next.next;
        int count = 1;
        while (p1 != p2) {
            if (p2.next != null && p2.next.next!= null)
            {
                p1 = p1.next;
                p2 = p2.next.next;
            }
            else
                return null;
        }
        p1 = pHead;
        while (p1 != p2)
        {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }
}

5张扑克牌是否是连续的

有对子也要返回false,0可以当作任何牌

import java.util.Arrays;

public class Solution {
    public boolean isContinuous(int [] numbers) {
        if (numbers == null || numbers.length != 5 )
            return false;
        Arrays.sort(numbers);
        int zeroSum = 0;
        int blank = 0;
        for (int i = 0; i < numbers.length - 1; i++)
        {
            if (numbers[i] == 0)
            {
                zeroSum++;
                continue;
            }
            if (numbers[i + 1] == numbers[i])
                return false;
            blank += numbers[i + 1] - numbers[i] - 1;
        }
        if (blank <= zeroSum)
            return true;
        return false;
    }
}

约瑟夫环

暴力解法,链表模拟

import java.util.LinkedList;
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        LinkedList<Integer> list = new LinkedList<>();
        for (int i = 0;i < n;i++)
        {
            list.add(i);
        }
        int index = 0;
        while (list.size() > 1)
        {
            index = (index + m - 1)%list.size();
            list.remove(index);
        }
        return list.size() == 1 ? list.get(0):-1;
    }
}

递推

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n < 1 || m < 1)
            return -1;
        if(n == 1)
            return 0;
        return (LastRemaining_Solution(n-1, m)+m)%n;
    }
}

求1 + 2 + … + n

不能使用条件语句

public class Solution {
    public static int Sum_Solution(int n) {
        int sum = n;
        boolean flag = (sum > 0) && ((sum += Sum_Solution(--n))> 0);
        return sum;
    }
}

不用加减乘除做加法

public class Solution {
    public int Add(int num1,int num2) {
        do {
            int sum = num1 ^ num2; //异或一次
            int temp = (num1 & num2)<<1;//确定进位
            num1 = sum;
            num2 = temp;
        }
        while (num2 != 0);
            return num1;
    }
}

把字符串转换为数字

 public class Solution {
    public static int StrToInt(String str) {
        if (str == null)
            return 0;
        str = str.trim();
        if (str.length() == 0)
            return 0;
        int flag = 1;
        int index = 0;
        if (str.charAt(0) == '+')
            index++;
        if (str.charAt(0) == '-')
        {
            flag = -1;
            index++;
        }
        int sum = 0;
        for (int i = index; i < str.length(); i++)
        {
            if (str.charAt(i) < '0' || str.charAt(i) > '9')
                return 0;
            int n = flag > 0?((int)(str.charAt(i) - '0')):((int)('0' - str.charAt(i)));
            sum = sum * 10 + n;
            //判断是否出借,如果出界了,符号会发生反转
            if (sum >= 0 && flag < 0 || sum < 0 && flag > 0)
                return 0;
        }
        return sum;
    }
}

数组中重复的数字

利用数组本身的特性

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if (numbers == null || numbers.length == 0)
            return false;
        for (int i:numbers)
        {
            if (i < 0 || i >=length)
                return false;
        }
        for (int j = 0; j < numbers.length; j++)
        {
            while (j != numbers[j])
            {
                if (numbers[j] == numbers[numbers[j]])
                {
                    duplication[0] = numbers[j];
                    return true;
                }
                int temp = numbers[j];
                numbers[j] = numbers[temp];
                numbers[temp] = temp;
            }
        }
        return false;
    }
}

Hash法

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if (numbers == null || numbers.length == 0)
            return false;
        for (int i:numbers)
        {
            if (i < 0 || i >=length)
                return false;
        }
        boolean[] bool = new boolean[length];
        for (int i = 0; i < length; i++)
        {
            if (bool[numbers[i]] == true)
            {
                duplication[0] = numbers[i];
                return true;
            }
            else
                bool[numbers[i]] = true;
        }
        return false;
    }
}

把二叉树打印成多行

非递归写法

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

public class Solution {
    ArrayList<ArrayList<Integer>> allList = new ArrayList<>();
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        if (pRoot == null)
            return allList;
        Queue<TreeNode> queue = new LinkedList<>();
        int width = 0;
        int cur = 0;
        queue.offer(pRoot);
        while (!queue.isEmpty())
        {
            width = queue.size();
            cur = 0;
            ArrayList<Integer> list = new ArrayList<>();
            while (cur < width)
            {
                TreeNode node = queue.poll();
                list.add(node.val);
                if (node.left != null)
                    queue.offer(node.left);
                if (node.right !=null)
                    queue.offer(node.right);
                cur++;
            }
            allList.add(list);
        }
        return allList;
    }
}

递归写法

import java.util.ArrayList;

public class Solution {
    ArrayList<ArrayList<Integer>> allList = new ArrayList<>();
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        if (pRoot == null)
            return allList;
        dfs(pRoot,1,allList);
        return allList;
    }
    void dfs(TreeNode root, int depth, ArrayList<ArrayList<Integer>> list)
    {
        if (depth > list.size())
            list.add(new ArrayList<>());
        list.get(depth-1).add(root.val);
        if (root.left != null)
            dfs(root.left,depth+1,list);
        if (root.right != null)
            dfs(root.right,depth+1,list);
    }
}

字符串流中第一个不重复字符

public class Solution {
    //Insert one char from stringstream
    String s = "";
    char[] hash = new char[128];
    public void Insert(char ch)
    {
        s += ch;
        hash[ch]++;
    }
    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        for (int i = 0; i<s.length();i++)
        {
            if (hash[s.charAt(i)] == 1)
                return s.charAt(i);
        }
        return '#';
    }
}

之字型打印二叉树

法1:借用栈。通过左右子树进入栈的顺序不同构建从左到右和从右到左

import java.util.ArrayList;
import java.util.Stack;

public class Solution {
    ArrayList<ArrayList<Integer>> allList = new ArrayList<>();
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        if (pRoot == null)
            return allList;
        int layer = 1;
        Stack<TreeNode> stack1 = new Stack<>(); //存储奇数层
        Stack<TreeNode> stack2 = new Stack<>(); //存储偶数层
        stack1.push(pRoot);
        while (!stack1.isEmpty() || !stack2.isEmpty()) {
            ArrayList<Integer> list = new ArrayList<>();
            if (layer % 2 != 0) {
                while (!stack1.isEmpty()) {
                    TreeNode node = stack1.pop();
                    if (node != null) {
                        list.add(node.val);
                        if (node.left != null) stack2.push(node.left);
                        if (node.right != null) stack2.push(node.right);
                    }
                }
                layer++;
                allList.add(list);
            }
            else if (layer % 2 == 0) {
                while (!stack2.isEmpty()) {
                    TreeNode node2 = stack2.pop();
                    if (node2 != null) {
                        list.add(node2.val);
                        if (node2.right != null) stack1.push(node2.right);
                        if (node2.left != null) stack1.push(node2.left);
                    }
                }
                layer++;
                allList.add(list);
            }
        }
        return allList;
    }
}

法2:层次遍历,遇到偶数则反转列表

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    ArrayList<ArrayList<Integer>> allList = new ArrayList<>();
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        if (pRoot == null)
            return allList;
        int layer = 1;
        int cur;
        int width;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(pRoot);
        while (!queue.isEmpty()) {
            width = queue.size();
            cur = 0;
            ArrayList<Integer> list = new ArrayList<>();
                while (cur < width) {
                    TreeNode node = queue.poll();
                    list.add(node.val);
                    if (node.left != null) queue.offer(node.left);
                    if (node.right != null) queue.offer(node.right);
                    cur++;
                }
                if (layer %2 != 0)
                {
                    layer++;
                    allList.add(list);
                }
                else if (layer %2 == 0)
                {
                    layer++;
                    allList.add(reverseList(list));
                }
        }
        return allList;
    }
    public ArrayList<Integer> reverseList(ArrayList<Integer> list)
    {
        ArrayList<Integer> newList = new ArrayList<>();
        for (int i = list.size() - 1; i >= 0; i--)
        {
            newList.add(list.get(i));
        }
        return newList;
    }
}

股票的最大利润(数组中两数差最大值)

public class Solution {
    public static int getMaxDiff(int[] nums)
    {
        if (nums == null || nums.length<2)
            return 0;
        int min = nums[0];
        int maxDiff = nums[1] - min;
        for (int i = 2; i < nums.length; i++)
        {
            if (nums[i -1] < min)
                min = nums[i -1];
            int curDiff = nums[i] - min;
            if (curDiff > maxDiff)
                maxDiff = curDiff;
        }
        return maxDiff;
    }
    public static void main(String[] args) {
        int[] nums = {9,11,8,5,7,12,16,14};
        System.out.println(getMaxDiff(nums));
    }
}

数据流中的中位数

法1,大小堆

如果输入是奇数,中位数为前一部分的最大值

如果输入是偶数,中位数为大顶堆和小顶堆值之和的一半

两个堆应该始终满足:

大顶堆的堆顶元素,小于或者等于小顶堆的堆顶元素

大顶堆的元素个数过载与小顶堆的元素个数相等,或者多1

当两个堆元素和为偶数,为了让大顶堆中多一个元素,大顶堆->小顶堆->大顶堆

当元素和为奇数,此时小顶堆必须多一个元素,这样大顶堆和小顶堆的元素个数才能相等,大顶堆->小顶堆

import java.util.PriorityQueue;

public class Solution {

    PriorityQueue<Integer> minQueue = new PriorityQueue<>();
    PriorityQueue<Integer> maxQueue = new PriorityQueue<>((x,y)->(y-x));
    int count = 0;
    public void Insert(Integer num) {
        count++;
        maxQueue.offer(num);
        minQueue.offer(maxQueue.poll());
        if (count % 2 != 0)
            maxQueue.offer(minQueue.poll());
    }

    public Double GetMedian() {
        if (count % 2 == 0)
            return (double)(maxQueue.peek() + minQueue.peek())/2;
        else
            return (double)maxQueue.peek();
    }
}

二叉搜索树的第k个结点

直接递归

public class Solution {
    int count = 0;
    public TreeNode KthNode(TreeNode pRoot, int k)
    {
        if (pRoot!=null)
        {
            TreeNode LNode = KthNode(pRoot.left,k);
            if (LNode != null)
                return LNode;
            count++;
            if (count == k)
                return pRoot;
            TreeNode RNode = KthNode(pRoot.right,k);
            if (RNode != null)
                return RNode;
        }
        return null;
    }
}

借助列表

import java.util.ArrayList;

public class Solution {
    ArrayList<TreeNode> list = new ArrayList<>();
    public TreeNode KthNode(TreeNode pRoot, int k)
    {
        if (pRoot == null || k <=0)
            return null;
        dfs(pRoot);
        if (k > list.size())
            return null;
        return list.get(k-1);
    }
    public void dfs(TreeNode root)
    {
        if (root.left != null)
            dfs(root.left);
        list.add(root);
        if (root.right != null)
            dfs(root.right);
    }
}

滑动窗口最大值

双端队列

import java.util.ArrayDeque;
import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> list = new ArrayList<>();
        if (num == null || num.length <=0 || size == 0 || size > num.length)
            return list;
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        for (int i = 0; i < size; i++)
        {
            while (!deque.isEmpty() && num[i] > num[deque.peekLast()])
                deque.removeLast();
            deque.addLast(i);
        }
        for (int i = size; i < num.length; i++)
        {
            list.add(num[deque.peekFirst()]);
            while (!deque.isEmpty() &&  num[i] >= num[deque.peekLast()])
                deque.removeLast();
            if (!deque.isEmpty() &&  (i - deque.peekFirst()) >= size)
                deque.removeFirst();
            deque.addLast(i);
        }
        list.add(num[deque.peekFirst()]);
        return list;
    }
}

暴力解法

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer
            > maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> list = new ArrayList<>();
        if (num == null || size == 0 || num.length <= 0 || num.length < size)
            return list;
        int []max = new int[2];
        int i = 0;
        int j = size - 1;
        while (j <= num.length - 1)
        {
            max = findMax(num,i,j);
            list.add(max[1]);
            i++;j++;
        }
        return list;
    }
    public int[] findMax(int[] num, int l, int r)
    {
        int max = num[l];
        int maxIndex = l;
        for (int i = l; i <= r; i++)
        {
            if (num[i] > max)
            {
                max = num[i];
                maxIndex = i;
            }
        }
        return new int[]{maxIndex,max};
    }
}

你可能感兴趣的:(剑指offer,数据结构和算法)