剑指offer题目实现51~60(java实现)

文章包含了数组中的逆序对计算,使用排序和哈希表的方法;寻找两个链表的第一个公共节点,通过同时遍历或存储链表节点的方式解决;在排序数组中查找数字的不同解法,包括暴力和二分查找;二叉搜索树的第k大节点的查找;二叉树的深度计算,包括递归和层次遍历;判断二叉树是否平衡;数组中数字出现的次数,利用哈希表和位运算;寻找和为s的数字的解法,包括双指针和哈希表;字符串翻转的几种实现;数组左旋转的解决方案,包括整体反转和局部反转策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面试题51:数组中的逆序对

class Solution {
private:
    const int kmod = 1000000007;
public:
    int InversePairs(vector<int> data) {
        int ret = 0;
        int n = data.size();
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (data[i] > data[j]) {
                    ret += 1;
                    ret %= kmod;
                }
            }
        }

        return ret;
    }
};
//取中间
int mid = (left + right) / 2; 
//左右划分合并
merge(divide(left, mid, data, temp), divide(mid + 1, right, data, temp)); 
public class Solution {
    public int mod = 1000000007;
    public int mergeSort(int left, int right, int [] data, int [] temp){
        //停止划分
        if(left >= right)    
            return 0;
        //取中间
        int mid = (left + right) / 2; 
        //左右划分合并
        int res = mergeSort(left, mid, data, temp) + 
                    mergeSort(mid + 1, right, data, temp); 
        //防止溢出
        res %= mod;  
        int i = left, j = mid + 1;
        for(int k = left; k <= right; k++)
            temp[k] = data[k];
        for(int k = left; k <= right; k++){
            if(i == mid + 1)
                data[k] = temp[j++];
            else if(j == right + 1 || temp[i] <= temp[j])
                data[k] = temp[i++];
            //左边比右边大,答案增加
            else{ 
                data[k] = temp[j++];
                // 统计逆序对
                res += mid - i + 1; 
            }
        }
        return res % mod;
    }
    public int InversePairs(int [] array) {
        int n = array.length;
        int[] res = new int[n];
        return mergeSort(0, n - 1, array, res);
    }
}

合并排序里面,这里代码很精简

for(int k = left; k <= right; k++){
     if(i == mid + 1) data[k] = temp[j++]; 
     else if(j == right + 1 || temp[i] <= temp[j]) data[k] = temp[i++];

面试题52: 两个链表的第一个公共节点

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode l1 = pHead1, l2 = pHead2;
        while(l1 != l2){
            l1 = (l1==null)?pHead2:l1.next;
            l2 = (l2==null)?pHead1:l2.next;
        }
        return l1;
    }
import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         //创建集合set
        Setset = new HashSet<>();
        //先把链表1的结点全部存放到集合set中
        while (pHead1 != null) {
            set.add(pHead1);
            pHead1 = pHead1.next;
        }

        //然后访问链表2的结点,判断集合中是否包含链表2的结点,如果包含就直接返回
        while (pHead2 != null) {
            if (set.contains(pHead2))
                return pHead2;
            pHead2 = pHead2.next;
        }
        //如果集合set不包含链表2的任何一个结点,说明没有交点,直接返回null
        return null;
    }
}

面试题53:在排序数组中查找数字

暴力解法

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int count = 0;
        for(int i = 0; i < array.length; i++){
            if(k == array[i]){
             count++;
            }
        }

        return count;
    }
}
public class Solution {
    //二分查找
    private int bisearch(int[] data, double k){ 
        int left = 0;
        int right = data.length - 1;
        //二分左右界
        while(left <= right){ 
            int mid = (left + right) / 2;
            if(data[mid] < k)
                left = mid + 1;
            else if(data[mid] > k)
                right = mid - 1;
        }
        return left;
    }
    public int GetNumberOfK(int [] array , int k) {
        //分别查找k+0.5和k-0.5应该出现的位置,中间的部分就全是k
        return bisearch(array, k + 0.5) - bisearch(array, k - 0.5);
    }
}

class Solution {
    public int missingNumber(int[] nums) {
        //定义二分点,左右边界
        int index, i = 0, j = nums.length-1;
        //遍历
        while(i < j){
            index = (i + j) / 2;
            //索引和值相等,在右边
            if(nums[index] == index){
                i = index + 1;
            }
            //索引和值不相等,在左边
            else{
                j = index - 1;
            }
        }
        return i == nums[i] ? i+1: i;
    }
}

//题目:假设一个单调递增的数组里的每个元素都是整数并且是唯一的。请编程实
//现一个函数找出数组中任意一个数值等于其下标的元素。例如,在数组{-3, -1,
//1, 3, 5}中,数字3和它的下标相等。
 
public class IntegerIdenticalToIndex {
    public int getNumberSameAsIndex(int[] arr) {
        //异常检测
        if(arr==null || arr.length<=0)
            return -1;  //代表错误
        int low=0;
        int high=arr.length-1;
        while(low<=high) {
            int mid= (high+low)>>1;
            if(arr[mid]>mid)
                high=mid-1;
            else if(arr[mid]<mid)
                low=mid+1;
            else
                return mid;
        }
        return -1;
    }
}

面试题54:二叉搜索树的第k大节点

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

import java.util.*;
public class Solution {
    //记录返回的节点
    private TreeNode res = null;
    //记录中序遍历了多少个
    private int count = 0;
    public void midOrder(TreeNode root, int k){
        //当遍历到节点为空或者超过k时,返回
        if(root == null || count > k) 
            return;
        midOrder(root.left, k);
        count++;
        //只记录第k个
        if(count == k)  
            res = root;
        midOrder(root.right, k);
    }
    public int KthNode (TreeNode proot, int k) {
        midOrder(proot, k);
        if(res != null)
            return res.val;
        //二叉树为空,或是找不到
        else 
            return -1;
    }
}

面试题55:二叉树的深度

import java.util.*;
public class Solution {
    public int maxDepth (TreeNode root) {
        //空节点没有深度
        if(root == null) 
            return 0;
        //返回子树深度+1
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; 
    }
}
import java.util.*;
public class Solution {
    public int maxDepth (TreeNode root) {
        //空节点没有深度
        if(root == null) 
            return 0;
        //队列维护层次后续节点
        Queue<TreeNode> q = new LinkedList<TreeNode>(); 
        //根入队
        q.offer(root); 
        //记录深度
        int res = 0; 
        //层次遍历
        while(!q.isEmpty()){ 
            //记录当前层有多少节点
            int n = q.size(); 
            //遍历完这一层,再进入下一层
            for(int i = 0; i < n; i++){ 
                TreeNode node = q.poll();
                //添加下一层的左右节点
                if(node.left != null) 
                    q.offer(node.left);
                if(node.right != null)
                    q.offer(node.right);
            }
            //深度加1
            res++; 
        }
        return res; 
    }
}
public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root==null){
            return true;
        }
        if(Math.abs(depth(root.left)-depth(root.right))>1){
            return false;
        }
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
    
    private int depth(TreeNode root){
        if(root == null) return 0;
        return Math.max(depth(root.left),depth(root.right))+1;
    }
}
public class Solution {
    boolean res = true;
    public boolean IsBalanced_Solution(TreeNode root) {
        isbalance(root);
        return res;
    }
    public int isbalance(TreeNode root){
        if(root == null || !res)
            return 0;
        int left = isbalance(root.left);
        int right = isbalance(root.right);
        if(Math.abs(left-right) > 1)
            res = false;
        return 1 + Math.max(left,right);
    }
}

面试题56:数组中数字出现的次数

import java.util.*;
public class Solution {
    public int[] FindNumsAppearOnce (int[] array) {
        HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>(); 
        ArrayList<Integer> res = new ArrayList<Integer>();
        //遍历数组
        for(int i = 0; i < array.length; i++) 
            //统计每个数出现的频率
            if(!mp.containsKey(array[i]))
                mp.put(array[i], 1);
            else
                mp.put(array[i], mp.get(array[i]) + 1);
        //再次遍历数组
        for(int i = 0; i < array.length; i++) 
            //找到频率为1的两个数
            if(mp.get(array[i]) == 1) 
                res.add(array[i]);
        //整理次序
        if(res.get(0) < res.get(1)) 
            return new int[] {res.get(0), res.get(1)};
        else
            return new int[] {res.get(1), res.get(0)};
    }
}
import java.util.*;
public class Solution {
    public int[] FindNumsAppearOnce (int[] array) {
        int res1 = 0;
        int res2 = 0;
        int temp = 0;
        //遍历数组得到a^b
        for(int i = 0; i < array.length; i++) 
            temp ^= array[i];
        int k = 1;
        //找到两个数不相同的第一位
        while((k & temp) == 0) 
            k <<= 1;
        for(int i = 0; i < array.length; i++){
            //遍历数组,对每个数分类
            if((k & array[i]) == 0) 
                res1 ^= array[i];
            else
                res2 ^= array[i];
        }
        //整理次序
        if(res1 < res2) 
            return new int[] {res1, res2};
        else
            return new int[] {res2, res1};
    }
}

解法一:哈希表

    public static int singleNumber(int[] nums) {
        HashMap<Integer, Integer> num = new HashMap<>();
        //使用哈希映射统计数组中每个元素的出现次数。
        //对于哈希映射中的每个键值对,键表示一个元素,值表示其出现的次数。
        for (int key:nums) num.put(key,num.getOrDefault(key,0)+1);
        //使用Map.Entry遍历num哈希表来获得只出现了一次的元素
        for (Map.Entry<Integer, Integer> temp : num.entrySet()) {
          //  System.out.println("num key= " + temp.getKey() + " and value= " + temp.getValue());
            if (temp.getValue() == 1)return temp.getKey();
        }
        return 0;
    }

解法二:位运算的思路

//题目:在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。
//请找出那个只出现一次的数字。
public class NumberAppearingOnce {
    public static int findNumberAppearingOnce(int[] arr) {
        if(arr==null || arr.length<=0)
            throw new RuntimeException();
        int[] bitSum = new int[32];
        //用0填充数组
        for(int i=0;i<32;i++)
            bitSum[i]=0;
        for(int i=0;i<arr.length;i++) {
            int bitMask=1;
            for(int j=31;j>=0;j--) {
                //注意arr[i]&bitMask不一定等于1或者0,有可能等于00010000
                int bit=arr[i]&bitMask;  
                if(bit!=0)
                    //arr[i]此位置上也是1
                    bitSum[j]+=1;
                bitMask=bitMask<<1;
            }
        }
        int result=0;
        for(int i=0;i<32;i++) {
            result=result<<1;
            result+=(bitSum[i]%3);
            //result=result<<1;  //不能放在后面,否则最前面一位就没了
        }
        return result;
    }
}

面试题57:和为s的数字

暴力解法

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> result = new ArrayList<>();
        for(int i = 0; i < array.length; i++){
            for(int j = 0; j < array.length; j++){
                if(i == j){
                    continue;
                }
                if(sum == (array[i] + array[j])){
                    result.add(array[i]);
                    result.add(array[j]);
                    return result;
                }
            }
        }

        return result;
    }
}

时间复杂度 O(N*N),数据量太大的过不了

import java.util.*;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res = new ArrayList<Integer>();
        //创建哈希表,两元组分别表示值、下标
        HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>();
        //在哈希表中查找target-numbers[i]
        for(int i = 0; i < array.length; i++){
            int temp = sum - array[i];
            //若是没找到,将此信息计入哈希表
            if(!mp.containsKey(temp)){ 
                mp.put(array[i], i);
            }
            else{
                //取出数字添加
                res.add(temp);   
                res.add(array[i]);
                break;
            }
        }
        return res;
    }
}
import java.util.*;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res = new ArrayList<Integer>();
        //左右双指针
        int left = 0, right = array.length - 1;
        //对撞双指针
        while(left < right){
            //相加等于sum,找到目标
            if(array[left] + array[right] == sum){
                res.add(array[left]);
                res.add(array[right]);
                break;
            //和太大,缩小右边
            }else if(array[left] + array[right] > sum)
                right--;
            //和太小,扩大左边
            else
                left++;
        }
        return res;
    }
}

这部分等看了左程云的视频课再来看

class Solution {
    public int[][] findContinuousSequence(int target) {
        int i=1, j=1;//窗口左右边界
        int sum=0;//用来与target比较
        List<int[]> list=new ArrayList<>();//结果集合
        while(i<=target/2) {
            if(sum < target) {
                sum+=j;
                j++;
            } else if(sum > target) {
                sum-=i;
                i++;
            } else {
                int[] arr=new int[j-i];
                for(int k=i;k<j;k++) {
                    arr[k-i]=k;
                }
                list.add(arr);
                //记录一个结果之后,一定记得窗口右移
                sum-=i;
                i++;
            }
        }
        int[][] T=new int[list.size()][];
        return list.toArray(T);
    }
}

面试题58:翻转字符串

public class Solution {
    public String ReverseSentence(String str) {
        String[] arr =  str.split(" ");
        String[] result = new String[arr.length];

        for (int i = arr.length - 1; i >= 0; i--) {
            result[arr.length - 1 - i] = arr[i];
        }

        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < result.length; i++) {
            if (i != result.length - 1) {
                sb.append(result[i]);
                sb.append(" ");
            } else {
                sb.append(result[i]);
            }
        }

        return sb.toString();
    }
}
import java.util.*;
public class Solution {
    //字符串反转函数
    private void reverse(char [] c, int l, int h){
        //双指针反转
        while(l < h)
            swap(c, l++, h--);
    }
    //字符交换函数
    private void swap(char [] c, int l, int h){
        char temp = c[l];
        c[l] = c[h];
        c[h] = temp;
    }
    
    public String ReverseSentence(String str) {
        int n = str.length();
        char[] c = str.toCharArray();
        //第一次整体反转
        reverse(c, 0, n - 1);
        for(int i = 0; i < n; i++){
            int j = i;
            //以空格为界找到一个单词
            while(j < n && c[j] != ' ') 
                j++;
            //将这个单词反转
            reverse(c, i, j - 1); 
            i = j;
        }
        return new String(c);
    }
}
import java.util.*;
public class Solution {
    public String ReverseSentence(String str) {
        Stack<String> st = new Stack<String>();
        String[] temp = str.split(" ");
        //单词加入栈中
        for(int i = 0; i < temp.length; i++){
            st.push(temp[i]);
            st.push(" ");
        }
        StringBuilder res = new StringBuilder();
        //去掉最后一个空格
        if(!st.isEmpty())
            st.pop();
        //栈遵循先进后出,单词顺序是反的
        while(!st.isEmpty())
            res.append(st.pop());
        return res.toString();
    }
}
public class Solution {
    public String LeftRotateString(String str,int n) {
        //取余,因为每次长度为n的旋转数组相当于没有变化
        if(str.isEmpty() || str.length() == 0)
            return "";
        int m = str.length();
        n = n % m; 
        //第一次逆转全部元素
        char[] s = str.toCharArray();
        reverse(s, 0, m - 1); 
        //第二次只逆转开头m个
        reverse(s, 0, m - n - 1);
        //第三次只逆转结尾m个
        reverse(s, m - n, m - 1); 
        return new String(s);
    }
    //反转函数
    private void reverse(char[] s, int start, int end){ 
        while(start < end){
            swap(s, start++, end--);
        }
    }
    //交换函数
    private void swap(char[] s, int a, int b){ 
        char temp = s[a];
        s[a] = s[b];
        s[b] = temp;
    }
}
import java.util.*;
public class Solution {
    public String LeftRotateString(String str,int n) {
        //取余,因为每次长度为n的旋转数组相当于没有变化
        if(str.isEmpty() || str.length() == 0)
            return "";
        int m = str.length();
        //取余,因为每次长度为m的旋转数组相当于没有变化
        n = n % m; 
        StringBuilder res = new StringBuilder();
        //先遍历后面的,放到前面
        for(int i = n; i < m; i++)
            res.append(str.charAt(i));
        //再遍历前面的放到后面
        for(int i = 0; i < n; i++)
            res.append(str.charAt(i));
        return res.toString();
    }
}

面试题59:队列的最大值

暴力解法:

import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        int max = Integer.MIN_VALUE;

        //异常检测
        if (num == null || num.length == 0 || size == 0) {
            return result;
        }

        //暴力解法
        for (int i = 0; i < num.length - size + 1; i++) {
            for (int j = i; j - i < size; j++) {
                if (max < num[j]) {
                    max = num[j];
                }
            }

            result.add(max);
            max = Integer.MIN_VALUE;
        }

        return result;
    }
}

时间复杂度为:O(N*K)

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        // Vector类 是在 java 中可以实现自动增长的对象数组
        vector<int> ret;
        if (num.size() == 0 || size < 1 || num.size() < size) return ret;
        int n = num.size();

        for (int i = 0; i + size - 1 < n; ++i) {
            int j = i + size - 1;
            int max_val = num[j];
            for (int k = i; k < j; ++k) {
                max_val = max(max_val, num[k]);
            }
            ret.push_back(max_val);
        }
        return ret;
    }
};
class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int> ret;
        if (num.size() == 0 || size < 1 || num.size() < size) return ret;
        int n = num.size();
           deque<int> dq;
           for (int i = 0; i < n; ++i) {    
               //dq.back() dq 最后一个元素索引
               while (!dq.empty() && num[dq.back()] < num[i]) {
                   //弹出并删除 dq 最后一个元素
                   dq.pop_back();
               }
               //添加到 dq 末尾
               dq.push_back(i);
               // 判断队列的头部的下标是否过期
               //dq.front() 第一个元素的索引
               if (dq.front() + size <= i) {
                   //弹出并删除 dq 第一个元素
                   dq.pop_front();
            }
            // 判断是否形成了窗口
               if (i + 1 >= size) {
                   //如果形成了窗口,将该窗口最大值添加到结果数组中
                   ret.push_back(num[dq.front()]);
               }
           }
           return ret; 
    }
};

简述Java中的Deque<E>(双端队列)接口

1 定义

Interface Deque<E>:继承了Queue接口,支持两端元素插入和移除的线性集合。

2 特性

既具有队列FIFO(先进先出)特点又具有栈LIFO(后进先出)特点,既是队列又是栈

3 常用方法

public void offerFirst(E e):向队头插入元素

public void offerLast(E e):向队尾插入元素

public E pollFirst():获取并移除队头元素

public E pollLast():获取并移除队尾元素

public E peekFirst():获取队头元素

public E peekLast():获取队尾元素

public int size():获取队列中的元素个数

4 栈操作

队头插入队头删除(后进先出):

public void push(E e) :向队头插入元素

public E pop() :检索并删除队头元素

public E peek():检索但不删除队头元素

5 队列操作

队尾插入队头删除(先进先出):

boolean offer(E e):向队尾插入元素

public E poll() :检索并删除队头元素

public E peek():检索但不删除队头元素

6 两个主要实现类

ArrayDeque: 基于数组实现的线性双端队列;

LinkedList: 基于链表实现的链式双端队列。

class MaxQueue {
    int[] q = new int[20000];
    int begin = 0, end = 0;

    public MaxQueue() {

    }
    
    public int max_value() {
        int ans = -1;
        for (int i = begin; i != end; ++i) {
            ans = Math.max(ans, q[i]);
        }
        return ans;
    }
    
    public void push_back(int value) {
        q[end++] = value;
    }
    
    public int pop_front() {
        if (begin == end) {
            return -1;
        }
        return q[begin++];
    }
}
class MaxQueue {
    Queue<Integer> q;
    Deque<Integer> d;

    public MaxQueue() {
        q = new LinkedList<Integer>();
        d = new LinkedList<Integer>();
    }
    
    public int max_value() {
        if (d.isEmpty()) {
            return -1;
        }
        return d.peekFirst();
    }
    
    //往队列末尾插入元素
    public void push_back(int value) {
        while (!d.isEmpty() && d.peekLast() < value) {
            d.pollLast();
        }
        d.offerLast(value);
        q.offer(value);
    }
    
    public int pop_front() {
        if (q.isEmpty()) {
            return -1;
        }
        int ans = q.poll();
        if (ans == d.peekFirst()) {
            d.pollFirst();
        }
        return ans;
    }
}

面试题60:n个骰子的点数

class Solution {
    public double[] dicesProbability(int n) {
        double[] dp = new double[6];
        Arrays.fill(dp, 1.0 / 6.0);
        for (int i = 2; i <= n; i++) {
            double[] tmp = new double[5 * i + 1];
            for (int j = 0; j < dp.length; j++) {
                for (int k = 0; k < 6; k++) {
                    tmp[j + k] += dp[j] / 6.0;
                }
            }
            dp = tmp;
        }
        return dp;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值