面试必考真题

1.反转链表

public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode curr = head;
        ListNode pre = null;
        while(curr!=null){
            ListNode next = curr.next;
            curr.next = pre;
            pre = curr;
            curr = next;
        }
        return pre;
    }
}

思路:

首先需要定义一个pre空节点,作为反转前头节点的指向节点。

循环中,首先要保存一下当前节点的下一个节点为next,再改变curr节点的指向(反转操作)。

反转后,依次移动pre,curr。

最后一次循环后,curr已经指向空,因此跳出循环,返回pre(反转后的头节点)

 

2.请实现有重复数字的有序数组的二分查找。

输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
public class Solution {
    public int upper_bound_ (int n, int v, int[] a) {
        if(v>a[n-1]) return n+1;
        int l=0,h=n-1;
        while(l<h){
            int m = l+(h-l)/2;
            if(a[m]<v){
                l = m+1;
            }else{
                h = m;
            }
        }
        return l+1;       
    }
}

思路:n是数组长度,v是指定的数。

这道题主要是可能存在重复数字,要求输出在数组中第一个大于等于查找值的位置。

因此只要遍历到的数等于或者大于指定值,都要继续缩小范围继续查找。

因为遍历到的数等于指定值时,有可能它左边还有一串跟它一样的数,因此需要再次二分查找直到找到第一个比它小的数。

最后返回l+1,也就是第一个大于等于查找值的位置。

 

5.判断给定的链表中是否有环

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head==null) return false;
        ListNode slow = head;
        ListNode fast = head;
        while(fast!=null && fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow==fast) return true;
        }
        return false;   
    }
}

思路:快指针+慢指针,注意循环条件是快指针不为null且快指针的下一个节点也不为null。

 

6.合并两个有序链表

public class Solution {
    public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(-1);
        ListNode curr = pre;
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                curr.next = l1;
                l1 = l1.next;               
            }else{
                curr.next = l2;
                l2 = l2.next;
            }
            curr = curr.next;            
        }
        curr.next = l1==null?l2:l1;
        return pre.next; 
    }
}

思路:我发现定义pre节点可以避免处理head为空的问题!

思路就是每次循环比较两个链表的头节点的值的大小,然后接哪个就把哪个的头后一位。注意无论接了哪个,最后总链表的curr都要后移一位。

然后最后要处理其中一个链表已经接完但是另一个链表还没有接完的情况。

 

8.给定一个链表,删除链表的倒数第n个节点并返回链表的头指针

public class Solution {
    public ListNode removeNthFromEnd (ListNode head, int n) {
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode slow = pre;
        ListNode fast = pre;
        for(int i=0;i<n;i++){
            fast = fast.next;          
        }
        while(fast.next!=null){
            slow = slow.next;
            fast = fast.next;
        }
        slow.next = slow.next.next;
        return pre.next;       
    }
}

思路:双指针,让快指针先走n步。然后两条指针再同时走,直到快指针走到当前链表的最后一个节点时(此时慢指针的next就是需要删除的节点)退出循环,删除慢指针的next节点。

需要注意的是,需要事先定义一个pre节点指向一开始的head,否则无法处理head节点被删除的情况以及链表长度为0和1的情况。最后返回pre.next即可。

 

10.两个栈实现队列

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);      
    }
    
    public int pop() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();   
    }
}

思路:push操作就直接用栈1push就行了。

pop的时候,想象一下如果这个"队列“刚刚存完数据,那么栈1应该有数据,栈2为空。此时我们应该把栈1的数据依次pop然后push到栈2中(此时数据已经颠倒顺序),然后栈2pop一个数,就完成了一次”队列“的pop。

如果这个:“队列”已经pop过数据,那么栈2可能不为空,则不需要从栈1中pop到栈2,直接从栈2中pop数据(顺序已经是颠倒的)即可。

 

17.两数相加:给出一个整数数组,请在数组中找出两个加起来等于目标值的数,

你给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2.。注意:下标是从1开始的
public class Solution {
    public int[] twoSum (int[] numbers, int target) {
        Map<Integer,Integer> hm = new HashMap<>();
        int n = numbers.length;
        for(int i=0;i<n;i++){
            int res = target - numbers[i];
            if(hm.containsKey(res)) return new int[]{hm.get(res)+1,i+1};
            hm.put(numbers[i],i);        
        }
        return new int[]{-1,-1};
    }
}

思路:一遍hashmap是最简单的。

创建一个hashmap,key是数组中的数,value是该数下标。

遍历数组,每次遍历时计算target-numbers[i],然后先查找这个r差值res是否已经存在hashmap中,若存在则直接返回两个下标,若不存在把当前遍历到的数和它下标put到hashmap中。

注意这道题下标从1开始而不是0。注意运用containsKey方法和put方法。

 

18.跳台阶。一次可跳1或2级,求跳到第n级共几种跳法。

public class Solution {
    public int JumpFloor(int target) {
        if(target==1) return 1;
        if(target==2) return 2;
        int [] array = new int[target];
        array[0]=1;
        array[1]=2;
        for(int i=2;i<target;i++){
            array[i] = array[i-1]+array[i-2];
        }
        return array[target-1];
    }
}

思路:简单动态规划。先建立数组,然后寻找跳i阶的方法与i-1,i-2的关系,依次存入数组即可。

 

21.给定一个数组arr,返回子数组的最大累加和

例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子数组中,[3, 5, -2, 6]可以累加出最大的和12,所以返回12.
public class Solution {
    public int maxsumofSubarray (int[] arr) {
        int sum = 0;
        int res = arr[0];
        for(int num:arr){
            sum = Math.max(sum+num,num);
            res = Math.max(res,sum);
        }
        return res;    
    }
}

思路:这道题咋做啊,要不背下来吧。

sum表示算上当前数的最大累加和。

res表示在算上当前num的最大累加和和不算当前num的最大累加和里边取max。

真的不太懂,背下来吧。

 

26.合并两个有序数组

把B合并到A,假设A容量够大

public class Solution {
    public void merge(int A[], int m, int B[], int n) {
        int a = m-1, b = n-1, curr = m+n-1;
        while(a>=0 && b>=0){
            if(A[a]>B[b]){
                A[curr--] = A[a--];               
            }else{
                A[curr--] = B[b--];                
            }
        }
        while(b>=0) A[curr--] = B[b--];
    }
}

思路:3个指针,一个指向当前放置的位置,还有两根指针指向两个数组遍历到的位置。

要注意最后可能出现其中一个数组的数字已经被放完的情况,即a或b等于-1了,就会跳出大循环。

此时我们要把另一个可能还未放完的数组中的数放置规整,这里就不用判断了直接放就行。

如果是A没放完,那我们啥也不需要做,因为最后本来就是归并到A。

如果是B没放完,则while中判断b>=0,然后依次将B中剩余的数放入A。

 

30.反转字符串

public class Solution {
    public String solve (String str) {
        if(str==null) return null;
       int l = 0, h = str.length()-1;
        char[] array = str.toCharArray();
        while(l<h){
            char temp = array[l];
            array[l++] = array[h];
            array[h--] = temp;           
        }       
        String  result = new String(array);
        return result;
    }
}

思路:双指针。字符串转化成字符数组之后用双指针交换字符即可。注意字符数组可以直接转化程字符串,直接new就行。

 

22.合法的括号序列

import java.util.*;
public class Solution {
    public boolean isValid (String s) {      
        Stack stack = new Stack();
        int n = s.length();
        if(n%2==1) return false;
        for(int i=0;i<n;i++){
            if(s.charAt(i)=='('){
                stack.push(')');
            }else if(s.charAt(i)=='['){
                stack.push(']');                
            }else if(s.charAt(i)=='{'){
                stack.push('}');
            }else if(stack.isEmpty()||s.charAt(i)!=(char)stack.pop()){
                return false;              
            }         
        }
        return stack.isEmpty();        
    }
}

思路:本题基本思路为,遇到左括号就push一个对应的右括号,遇到右括号就看pop出来的是不是对应的右括号。

注意处理特殊情况:字符串长度为奇数直接返回false,首先出现无配对的右括号怎么办(最后一个else if分支判断首先判断栈是否非空,注意要比pop先判断),最后全部遍历完之和判断一下栈是否为空,不为空也是false。

 

23.数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
             HashMap<Integer,Integer> hm = new HashMap<>();
        for(int num:array){
            if(hm.containsKey(num)){
                hm.remove(num);
            }else{
                hm.put(num,num);
            }
        }
        ArrayList<Integer>list = new ArrayList<>();
        for (int key : hm.keySet()){
            list.add(key);
        }
        num1[0]=list.get(0);
        num2[0]=list.get(1);
    }
}

思路:创建一个hashmap,然后遍历这个array。如果存在这个key,就移除。不存在就加入num,num键值对。这样出现两次的数就会被加入后移除。而只出现一次的数就会留在map中。其他就是暴力了。

 

31.求二叉树的最大深度

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

思路:递归头:root为null时return 0

递归体:取左右子树的最大深度+1

 

33.实现函数 int sqrt(int x).
计算并返回x的平方根
public class Solution {
  public int sqrt (int x) {
        if(x<=1) return x;
        int l = 1, h=x/2;
        while(l<=h){
            int m = l+(h-l)/2;
            int sqrt = x/m;
            if(sqrt==m){
                return m;
            }else if(sqrt>m){
                l = m+1;
            }else{
                h = m-1;
            }
        }
        return h;
    }
}

 思路:一个数 x 的开方 sqrt 一定在 0 ~ x 之间,并且满足 sqrt == x / sqrt。可以利用二分查找在 0 ~ x 之间查找 sqrt。

对于 x = 8,它的开方是 2.82842...,最后应该返回 2 而不是 3。在循环条件为 l <= h 并且循环退出时,h 总是比 l 小 1,也就是说 h = 2,l = 3,因此最后的返回值应该为 h 而不是 l。

 

35.求二叉树的镜像

public class Solution {
    public void Mirror(TreeNode root) {
        if(root == null) return;
        TreeNode tmp;
        tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        Mirror(root.left);
        Mirror(root.right);        
    }
}

思路:退出条件+操作+递归调用

 

39.输出斐波那契数列的第n项

(从0开始,第0项为0,第1项是1)

public class Solution {
    public int Fibonacci(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        int[] array = new int[n+1];
        array[0] = 0;
        array[1] = 1;
        for(int i=2;i<n+1;i++){
            array[i] = array[i-1]+array[i-2];
        }
        return array[n];      
    }
}

 

 

46.判断一个链表是否为回文结构

public class Solution {
    public boolean isPail (ListNode head) {
        ListNode curr = head;
        List<Integer> list = new ArrayList<>();
        while(curr!=null){
            list.add(curr.val);
            curr = curr.next;           
        }
        int n = list.size();
        int l = 0, h = n-1;
        while(l<h){
            if(!list.get(l++).equals(list.get(h--))) return false;
        }
        return true;   
    }
}

思路:我选择把链表转换为ArrayList之后再用双指针。

判断的时候必须用equals方法,不要用==,不然[-129,-129]会报错!为啥??

还有ArrayList取数不能直接用下标,要用get方法。放数字要用add方法。获取长度用size方法。

 

 
posted @ 2020-10-01 20:17  菅兮徽音  阅读(146)  评论(0)    收藏  举报