Java算法:LeetCode算法Java版,百度公司算法笔试面试题LeetCode整理,百度校招算法刷题,百度算法Java版,Java版快速排序及优化后的快排,归并排序、堆排序,百度算法20道题

最近要参加百度公司2020春招的校招面试,特整理了下百度公司的算法题笔试面试题,以此纪念百度对我的认可。资源来源于LeetCode,纯手打,喜欢请支持正版。

再次感谢百度!

 

目录

两数之和... 1

两数相加... 1

无重复字符的最长子串... 2

两个排序数组的中位数... 3

最长回文子串... 4

有效的括号... 5

搜索旋转排序数组... 6

旋转图像... 6

螺旋矩阵... 8

编辑距离... 10

删除排序数组中的重复项... 11

合并两个有序数组... 12

环形链表... 13

二又树的后序遍历... 13

LRU缓存机制... 14

排序链表... 15

求众数... 16

反转链表... 16

前K个高频元素... 17

寻找数组的中心索引... 17

两数之和

给定一个整数数组和一个目标值,找出数组中和为目标值的 两个 数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9

所以返回 [0, 1]

public int[] twoSum(int[] nums, int target) { 
    for (int i = 0; i < nums.length; i++) { 
        for (int j = i + 1; j < nums.length; j++) { 
            if (nums[j] == target - nums[i]) { 
                return new int[] { i, j }; 
            } 
        } 
    } 
    throw new IllegalArgumentException("No two sum solution"); 
}

 

两数相加

给定两个 非空 链表来表示两个非负整数。位数按照 逆序 方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

示例:

输入: (2 -> 4 -> 3) + (5 -> 6 -> 4)

输出: 7 -> 0 -> 8

原因: 342 + 465 = 807

public class ListNode { 
    public int val; 
    public ListNode next; 
    public ListNode(int i) this.val = i;     
    public int val() return val;  
} 
public ListNode addTwoNumbers(ListNode l1, ListNode l2) { 
    ListNode dummyHead = new ListNode(0); 
    ListNode p = l1, q = l2, curr = dummyHead; 
    int carry = 0; 
    while (p != null || q != null) { 
        int x = (p != null) ? p.val : 0; 
        int y = (q != null) ? q.val : 0; 
        int sum = carry + x + y; 
        carry = sum / 10; 
        curr.next = new ListNode(sum % 10); 
        curr = curr.next; 
        if (p != null) p = p.next; 
        if (q != null) q = q.next; 
    } 
    if (carry > 0) curr.next = new ListNode(carry);      
    return dummyHead.next; 
}

 

无重复字符的最长子串

给定一个字符串,找出不含有重复字符的 最长子串 的长度。

示例:

给定 `"abcabcbb"` ,没有重复字符的最长子串是 `"abc"` ,那么长度就是3。

给定 `"bbbbb"` ,最长的子串就是 `"b"` ,长度是1。

给定 `"pwwkew"` ,最长子串是 `"wke"` ,长度是3。请注意答案必须是一个 子串 , `"pwke"` 是 _子序列_ 而不是子串。

public class Solution { 
    public int lengthOfLongestSubstring(String s) { 
        int n = s.length(); 
        Set<Character> set = new HashSet<>(); 
        int ans = 0, i = 0, j = 0; 
        while (i < n && j < n) { 
            //尽量扩大范围[i, j]
            if (!set.contains(s.charAt(j))){ 
                set.add(s.charAt(j++)); 
                ans = Math.max(ans, j - i); 
            } 
            else set.remove(s.charAt(i++));            
        } 
        return ans; 
    } 
}

 

两个排序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。

请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。

示例 1:

nums1 = [1, 3]

nums2 = [2]

中位数是 2.0

示例 2:

nums1 = [1, 2]

nums2 = [3, 4]

中位数是 (2 + 3)/2 = 2.5

class Solution { 
    public double findMedianSortedArrays(int[] A, int[] B) { 
        int m = A.length; 
        int n = B.length; 
        if (m > n) { // 确保 m<=n 
            int[] temp = A; A = B; B = temp; 
            int tmp = m; m = n; n = tmp; 
        } 
        int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2; 
        while (iMin <= iMax) { 
            int i = (iMin + iMax) / 2; 
            int j = halfLen - i; 
            if (i < iMax && B[j-1] > A[i]) iMin = iMin + 1;             
            else if (i > iMin && A[i-1] > B[j]) iMax = iMax - 1;  
            else { // i是最好的 
                int maxLeft = 0; 
                if (i == 0) { maxLeft = B[j-1]; } 
                else if (j == 0) { maxLeft = A[i-1]; } 
                else { maxLeft = Math.max(A[i-1], B[j-1]); } 
                if ( (m + n) % 2 == 1 ) { return maxLeft; } 
                int minRight = 0; 
                if (i == m) { minRight = B[j]; } 
                else if (j == n) { minRight = A[i]; } 
                else { minRight = Math.min(B[j], A[i]); } 
                return (maxLeft + minRight) / 2.0; 
            } 
        } 
        return 0.0; 
    } 
}

 

最长回文子串

给定一个字符串 s ,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。

示例 1:

输入: "babad"

输出: "bab"

注意: "aba"也是一个有效答案。

示例 2:

输入: "cbbd"

输出: "bb"

public String longestPalindrome(String s) { 
    if (s == null || s.length() < 1) return ""; 
    int start = 0, end = 0; 
    for (int i = 0; i < s.length(); i++) { 
        int len1 = expandAroundCenter(s, i, i); 
        int len2 = expandAroundCenter(s, i, i + 1); 
        int len = Math.max(len1, len2); 
        if (len > end - start) { 
            start = i - (len - 1) / 2; 
            end = i + len / 2; 
        } 
    } 
    return s.substring(start, end + 1); 
} 
private int expandAroundCenter(String s, int left, int right) { 
    int L = left, R = right; 
    while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) { 
        L--; 
        R++; 
    } 
    return R - L - 1; 
}

 

有效的括号

给定一个只包括 `'('` , `')'` , `'{'` , `'}'` , `'['` , `']'` 的字符串,判断字符串是否有效。

有效字符串需满足:

1.  左括号必须用相同类型的右括号闭合。

2.  左括号必须以正确的顺序闭合。

注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"

输出: true

示例 2:

输入: "()[]{}"

输出: true

示例 3:

输入: "(]"

输出: false

示例 4:

输入: "([)]"

输出: false

示例 5:

输入: "{[]}"

输出: true

public boolean isValid(String s) { 
        Deque<Character> stack = new ArrayDeque<>(); 
        for (int i = 0; i < s.length(); i++) { 
            if (s.charAt(i) == '(' || s.charAt(i) == '{' || s.charAt(i) == '[') { 
                stack.push(s.charAt(i)); 
            } else { 
                if (stack.isEmpty()) return false; 
                else { 
                    if (stack.peek() == '(' && s.charAt(i) != ')') { 
                        return false; 
                    } else if (stack.peek() == '{' && s.charAt(i) != '}') { 
                        return false; 
                    } else if (stack.peek() == '[' && s.charAt(i) != ']') { 
                        return false; 
                    } 
                    stack.pop(); 
                } 
            } 
        } 
        return stack.isEmpty(); 
}

 

搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 `[0,1,2,4,5,6,7]` 可能变为 `[4,5,6,7,0,1,2]` )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 `-1` 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 _O_ (log _n_ ) 级别。

示例 1:

输入: nums = [`4,5,6,7,0,1,2]`, target = 0

输出: 4

示例 2:

输入: nums = [`4,5,6,7,0,1,2]`, target = 3

输出: -1

public static class Solution2 { 
	public int search(int[] nums, int target) { 
		if (nums == null || nums.length == 0) return -1; 		
		int lo = 0; 
		int hi = nums.length - 1; 
		while (lo < hi) { 
			int mid = (lo + hi) / 2; 
			if (nums[mid] == target) return mid;  
			if (nums[lo] <= nums[mid]) { 
				if (target >= nums[lo] && target < nums[mid]) {
					hi = mid - 1; 
				} else { 
					lo = mid + 1; 
				} 
			} else { 
				if (target > nums[mid] && target <= nums[hi]) { 
					lo = mid + 1; 
				} else { 
					hi = mid - 1; 
				} 
			} 
		} 
		return nums[lo] == target ? lo : -1; 
	} 
}

 

旋转图像

给定一个 _n_ × _n_ 的二维矩阵表示一个图像。

将图像顺时针旋转 90 度。

说明:

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。 请不要 使用另一个矩阵来旋转图像。

示例 1:

给定 matrix =

[

  [1,2,3],

  [4,5,6],

  [7,8,9]

],

原地旋转输入矩阵,使其变为:

[

  [7,4,1],

  [8,5,2],

  [9,6,3]

]

示例 2:

给定 matrix =

[

  [ 5, 1, 9,11],

  [ 2, 4, 8,10],

  [13, 3, 6, 7],

  [15,14,12,16]

],

原地旋转输入矩阵,使其变为:

[

  [15,13, 2, 5],

  [14, 3, 4, 1],

  [12, 6, 8, 9],

  [16, 7,10,11]

]

public class Solution {
    public static class Solution1 {
        public void rotate(int[][] matrix) {
            //第一步:将矩阵转置,[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
            int m = matrix.length;
            for (int i = 0; i < m; i++) {
                for (int j = i; j < m; j++) {                    
                    int tmp = matrix[i][j];
                    matrix[i][j] = matrix[j][i];
                    matrix[j][i] = tmp;
                }
            }
            //第一步得到的结果为:[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
            //第二步保持矩阵的横坐标不变,对称交换纵坐标
            for (int i = 0; i < m; i++) {
                int left = 0;
                int right = m - 1;
                while (left < right) {
                    int tmp = matrix[i][left];
                    matrix[i][left] = matrix[i][right];
                    matrix[i][right] = tmp;
                    left++;
                    right--;
                }
            }
        }
    }
    public static class Solution2 {
        public void rotate(int[][] matrix) {
            int m = matrix.length;
            int n = matrix[0].length;
            int top = 0;
            int bottom = n - 1;
            while (top < bottom) {
                int[] tmp = matrix[top];
                matrix[top] = matrix[bottom];
                matrix[bottom] = tmp;
                top++;
                bottom--;
            }
            for (int i = 0; i < m; i++) {
                for (int j = i + 1; j < n; j++) {
                    int tmp = matrix[i][j];
                    matrix[i][j] = matrix[j][i];
                    matrix[j][i] = tmp;
                }
            }
        }
    }
}

 

螺旋矩阵

给定一个包含 _m_ x _n_ 个元素的矩阵( _m_ 行, _n_ 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

示例 1:

输入:

[

 [ 1, 2, 3 ],

 [ 4, 5, 6 ],

 [ 7, 8, 9 ]

]

输出: [1,2,3,6,9,8,7,4,5]

示例 2:

输入:

[

  [1, 2, 3, 4],

  [5, 6, 7, 8],

  [9,10,11,12]

]

输出: [1,2,3,4,8,12,11,10,9,5,6,7]

public class Solution {
  public static class Solution1 {
    public List<Integer> spiralOrder(int[][] matrix) {
      List<Integer> result = new ArrayList();
      int row = matrix.length;
      if (row == 0) return result;      
      int rowStart = 0;
      int rowEnd = matrix.length - 1;
      int colStart = 0;
      int colEnd = matrix[0].length - 1;
      while (rowStart <= rowEnd && colStart <= colEnd) {
        //向右移动
        for (int j = colStart; j <= colEnd; j++) {
          result.add(matrix[rowStart][j]);
        }
        rowStart++;
        //向下
        for (int i = rowStart; i <= rowEnd; i++) {
          result.add(matrix[i][colEnd]);
        }
        colEnd--;
        //仅在 rowStart <= rowEnd时
        //向左走
        if (rowStart <= rowEnd) {
          for (int j = colEnd; j >= colStart; j--) {
            result.add(matrix[rowEnd][j]);
          }
        }
        rowEnd--;
        //仅在 colStart <= colEnd时
        //向上
        if (colStart <= colEnd) {
          for (int i = rowEnd; i >= rowStart; i--) {
            result.add(matrix[i][colStart]);
          }
        }
        colStart++;
      }
      return result;
    }
  }
}

 

编辑距离

给定两个单词 _word1_ 和 _word2_ ,计算出将 _word1_ 转换成 _word2_ 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

1.  插入一个字符

2.  删除一个字符

3.  替换一个字符

示例 1:

输入: word1 = "horse", word2 = "ros"

输出: 3

解释:

horse -> rorse (将 'h' 替换为 'r')

rorse -> rose (删除 'r')

rose -> ros (删除 'e')

示例 2:

输入: word1 = "intention", word2 = "execution"

输出: 5

解释:

intention -> inention (删除 't')

inention -> enention (将 'i' 替换为 'e')

enention -> exention (将 'n' 替换为 'x')

exention -> exection (将 'n' 替换为 'c')

exection -> execution (插入 'u')

public class Solution {
    public static class Solution1 {
        public int minDistance(String word1, String word2) {
            int m = word1.length();
            int n = word2.length();
            if (m == 0) return n;            
            if (n == 0) return m;            
            char[] str1 = word1.toCharArray();
            char[] str2 = word2.toCharArray();
            int[][] table = new int[m + 1][n + 1];
            for (int i = 0; i < m + 1; i++) table[i][0] = i;            
            for (int j = 0; j < n + 1; j++) table[0][j] = j;            
            for (int i = 1; i < m + 1; i++) {
                for (int j = 1; j < n + 1; j++) {
                    int cost = 0;
                    if (str1[i - 1] != str2[j - 1]) cost = 1;                    
                    table[i][j] = Math.min(Math.min(table[i - 1][j] + 1, 
                                table[i][j - 1] + 1),
                                table[i - 1][j - 1] + cost);
                }
            }
            return table[m][n];
        }
    }
}

 

删除排序数组中的重复项

给定一个排序数组,你需要在删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定 _nums_ = [1,1,1,2,2,3],

函数应返回新长度 length = `5`, 并且原数组的前五个元素被修改为 `1, 1, 2, 2,` 3 。

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 _nums_ = [0,0,1,1,1,1,2,3,3],

函数应返回新长度 length = `7`, 并且原数组的前五个元素被修改为 `0`, 0, 1, 1, 2, 3, 3 。

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以 “引用” 方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝

int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。

// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。

for (int i = 0; i < len; i++) {

    print(nums[i]);

}

public class Solution {
  public static class Solution1 {
    public int removeDuplicates(int[] nums) {
      int counter = 0;
      int len = nums.length;
      if (len == 0) return 0;      
      if (len == 1) return 1;      
      if (len == 2) return 2;      
      List<Integer> a = new ArrayList();
      a.add(nums[0]);
      a.add(nums[1]);
      for (int i = 2; i < len; i++) {
        if (nums[i] != nums[i - 1]) {
          a.add(nums[i]);
        } else if (nums[i] != nums[i - 2]) {
          a.add(nums[i]);
        }
      }
      counter = a.size();
      for (int i = 0; i < counter; i++) {
        nums[i] = a.get(i);
      }
      return counter;
    }
  }
}

 

合并两个有序数组

给定两个有序整数数组 _nums1_ 和 _nums2_ ,将 _nums2_ 合并到 _nums1_ 中 _,_ 使得 _num1_ 成为一个有序数组。

说明:

*   初始化 _nums1_ 和 _nums2_ 的元素数量分别为 _m_ 和 _n_ 。

*   你可以假设 _nums1_ 有足够的空间(空间大小大于或等于 _m + n_ )来保存 _nums2_ 中的元素。

示例:

输入:

nums1 = [1,2,3,0,0,0], m = 3

nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]

public class Solution {
  public static class Solution1 {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
      int i = m - 1;
      int j = n - 1;
      int k = m + n - 1;
      while (i >= 0 && j >= 0) {
        if (nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
        else nums1[k--] = nums2[j--];
      }
      while (j >= 0) nums1[k--] = nums2[j--];
    }
  }
}

 

环形链表

给定一个链表,判断链表中是否有环。

进阶: 

你能否不使用额外空间解决此题?

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

 

二又树的后序遍历

给定一个二叉树,返回它的 _后序_ 遍历。

示例:

输入: [1,null,2,3] 

   1

     \

      2

     /

   3

输出: [3,2,1]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

public static class Solution2 { 
    public List<Integer> postorderTraversal(TreeNode root) { 
        List<Integer> result = new ArrayList(); 
        return post(root, result); 
    } 
    List<Integer> post(TreeNode root, List<Integer> result) { 
        if (root == null) return result;         
        post(root.left, result); 
        post(root.right, result); 
        result.add(root.val); 
        return result; 
    } 
}

 

LRU缓存机制

运用你所掌握的数据结构,设计和实现一个 [LRU (最近最少使用) 缓存机制] 。它应该支持以下操作: 获取数据 `get` 和 写入数据 `put` 。

获取数据 `get(key)` \- 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 

写入数据 `put(key, value)` \- 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);

cache.put(2, 2);

cache.get(1);       // 返回  1

cache.put(3, 3);    // 该操作会使得密钥 2 作废

cache.get(2);       // 返回 -1 (未找到)

cache.put(4, 4);    // 该操作会使得密钥 1 作废

cache.get(1);       // 返回 -1 (未找到)

cache.get(3);       // 返回  3

cache.get(4);       // 返回  4

解法一:

public class Solution1 { 
    public class LRUCache { 
        private Map<Integer, Integer> cache; 
        private final int max; 
        public LRUCache(int capacity) { 
            max = capacity; 
            cache = new LinkedHashMap<Integer, Integer>(capacity, 1.0f, true) { 
                public boolean removeEldestEntry(Map.Entry eldest) { 
                    return cache.size() > max; 
                } 
            }; 
        } 
        public int get(int key) { 
            return cache.getOrDefault(key, -1); 
        } 
        public void set(int key, int value) { 
            cache.put(key, value); 
        } 
    } 
}

看得一脸懵逼,解法二:

class Solution2 {
    public static class LRUCache {
        private class Node {
            int key;
            int value;
            LRUCache.Node prev;
            LRUCache.Node next;
            Node(int k, int v) {
                this.key = k;
                this.value = v;
            }
            Node() {
                this.key = 0;
                this.value = 0;
            }
        }
        private int capacity;
        private int count;
        private LRUCache.Node head;
        private LRUCache.Node tail;
        private Map<Integer, LRUCache.Node> map;
        // 值应该是Node类型!这就是拥有一个名为Node的类的全部意义!
        public LRUCache(int capacity) {
            this.capacity = capacity;
            this.count = 0;//我们需要一个计数来跟踪缓存中元素的数量
            //把LRU从缓存中推出去
            this.map = new HashMap();
            head = new LRUCache.Node();
            tail = new LRUCache.Node();
            head.next = tail;
            tail.prev = head;
        }
        public int get(int key) {
            LRUCache.Node node = map.get(key);
            //HashMap允许值为null,这比HashTable更好
            if (node == null) return -1;
            else {
                remove(node);
                add(node);
                return node.value;
            }
        }
        public void set(int key, int value) {
            LRUCache.Node node = map.get(key);
            if (node == null) {
                node = new LRUCache.Node(key, value);
                map.put(key, node);
                add(node);
                count++;
                if (count > capacity) {
                    LRUCache.Node toDelete = tail.prev;
                    map.remove(toDelete.key);
                    remove(toDelete);
                    count--;
                }
            } else {
                remove(node);
                node.value = value;
                add(node);
            }
        }
        private void remove(LRUCache.Node node) {
            LRUCache.Node next = node.next;
            LRUCache.Node prev = node.prev;
            prev.next = next;
            next.prev = prev;
        }
        private void add(LRUCache.Node node) {
            //将节点添加到第一个位置:head.next!!
            LRUCache.Node next = head.next;
            head.next = node;
            node.next = next;
            node.prev = head;
            next.prev = node;
        }
    }
}

 

排序链表

在 _O_ ( _n_ log _n_ ) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3

输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0

输出: -1->0->3->4->5

public class Solution {
    public static class Solution1 {
        public ListNode sortList(ListNode head) {
            if (head == null || head.next == null) return head;            
            //步骤1:把链表分成两半
            ListNode prev = null;
            ListNode slow = head;
            ListNode fast = head;
            while (fast != null && fast.next != null) {
                prev = slow;
                fast = fast.next.next;
                slow = slow.next;
            }
            prev.next = null;
            //步骤2:对每一半进行排序
            ListNode l1 = sortList(head);
            ListNode l2 = sortList(slow);
            //步骤3: 合并两部分
            return merge(l1, l2);
        }
        private ListNode merge(ListNode l1, ListNode l2) {
            ListNode result = new ListNode(0);
            ListNode tmp = result;
            while (l1 != null && l2 != null) {
                if (l1.val < l2.val) {
                    tmp.next = l1;
                    l1 = l1.next;
                } else {
                    tmp.next = l2;
                    l2 = l2.next;
                }
                tmp = tmp.next;
            }
            if (l1 != null) tmp.next = l1;            
            if (l2 != null) tmp.next = l2;            
            return result.next;
        }
    }
}

 

求众数

给定一个大小为 _n_ 的数组,找到其中的众数。众数是指在数组中出现次数 大于 `⌊ n/2 ⌋` 的元素。

你可以假设数组是非空的,并且给定的数组总是存在众数。

示例 1:

输入: [3,2,3]

输出: 3

示例 2:

输入: [2,2,1,1,1,2,2]

输出: 2

public static class Solution3 {
    //This is O(nlogn) time.
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);//就这么简单
        return nums[nums.length / 2];
    }
}

 

反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL

输出: 5->4->3->2->1->NULL

进阶: 

你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

public static class Solution1 {
    public ListNode reverseList(ListNode head) {
        ListNode newHead = null;
        while (head != null) {
            ListNode next = head.next;
            head.next = newHead;
            newHead = head;
            head = next;
        }
        return newHead;
    }
}
/*    //测试代码
    public static void main(String[] args) {
        Node n = new Node(1);
        n.appendToTail(2);
        n.appendToTail(3);
        n.appendToTail(4);
        n.appendToTail(5);
        System.out.println("反转前:" + n);
        System.out.println("反转后:" + reverseList(n));
    }
    public static Node reverseList(Node head) {
        Node newHead = null;
        while (head != null) {
            Node next = head.next;
            head.next = newHead;
            newHead = head;
            head = next;
        }
        return newHead;
    }
    static class Node {
        int data;
        Node next = null;
        Node(int d) data = d;
        @Override
        public String toString() return data + "->" + next;    
        void appendToTail(int d) {//添加数据到链表尾部
            Node end = new Node(d);
            Node n = this;
            while (n.next != null) n = n.next;
            n.next = end;
        }
    }*/

//方法二:递归
public class LinkedRevers {
    public static void main(String[] args) {
        Node node = new Node(1, new Node(2, new Node(3, new Node(4, new Node(5, new Node(6, null))))));
        System.out.println(new Node().reverse(node));//6->5->4->3->2->1->null
    }
}

class Node {
    public int value;
    public Node next;
    @Override
    public String toString() {
        return value + "->" + next;
    }
    public Node() {
    }
    public Node(int value, Node next) {
        this.value = value;
        this.next = next;
    }
    public Node reverse(Node head) {
        if (head == null || head.next == null) return head;
        Node temp = head.next;
        Node newHead = reverse(head.next);
        temp.next = head;
        head.next = null;
        return newHead;
    }
}

附:链表翻转问题

k个一组翻转链表
给出一个链表,每 _k_ 个节点一组进行翻转,并返回翻转后的链表。
_k_ 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 _k_ 的整数倍,那么将最后剩余节点保持原有顺序。
示例 :
给定这个链表: `1->2->3->4->5`
当 _k_ = 2 时,应当返回: `2->1->4->3->5`
当 _k_ = 3 时,应当返回: `3->2->1->4->5`
说明 :
*   你的算法只能使用常数的额外空间。
*   你不能只是单纯的改变节点内部的值 ,而是需要实际的进行节点交换。

public class Solution {
     public ListNode reverseKGroup(ListNode head, int k) {
         ListNode curr = head;
         int count = 0;
         while (curr != null && count != k) {
             curr = curr.next;
             count++;
         }
         if (count == k) {
             curr = reverseKGroup(curr, k);
             while (count-- > 0) {
                 ListNode temp = head.next;
                 head.next = curr;
                 curr = head;
                 head = temp;
             }
             head = curr;
         }
         return head;
    }
}

旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 _k_ 个位置,其中 _k_ 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: `2->0->1->NULL`
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: `0->1->2->NULL`
向右旋转 4 步: `2->0->1->NULL`
public class Solution {
  public static class Solution1 {
    public ListNode rotateRight(ListNode head, int k) {
      if (head == null) return head;
      ListNode copyHead = head;
      int len = 1;
      while (copyHead.next != null) {
        copyHead = copyHead.next;
        len++;
      }
      copyHead.next = head;//把尾和头连在一起做成一个圆圈
      for (int i = len - k % len; i > 1; i--) head = head.next;      
      copyHead = head.next;
      head.next = null;
      return copyHead;
    }
  }
}

反转链表Ⅱ
反转从位置 _m_ 到 _n_ 的链表。请使用一趟扫描完成反转。
说明:  
1 ≤ _m_ ≤ _n_ ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, _m_ = 2, _n_ = 4
输出: 1->4->3->2->5->NULL
public class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
      ListNode dummy = new ListNode(-1);
      dummy.next = head;
      ListNode pre = dummy;
      for (int i = 0; i < m - 1; i++) pre = pre.next;      
      ListNode start = pre.next;
      ListNode then = start.next;
      for (int i = 0; i < n - m; i++) {
        start.next = then.next;
        then.next = pre.next;
        pre.next = then;
        then = start.next;
      }
      return dummy.next;
    }
}

附:二叉树翻转问题

public static TreeNode reversTree(TreeNode root) {
        if (root == null) return null;
        if (root.left != null) reversTree(root.left);
        if (root.right != null) reversTree(root.right);
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        return root;
}

 

K个高频元素

给定一个非空的整数数组,返回其中出现频率前 _k_ 高的元素。

例如,

给定数组 `[1,1,1,2,2,3]` , 和 k = 2,返回 `[1,2]` 。

注意:

*   你可以假设给定的 _k_ 总是合理的,1 ≤ k ≤ 数组中不相同的元素的个数。

*   你的算法的时间复杂度 必须 优于 O( _n_ log _n_ ) , _n_ 是数组的大小。

public class Solution {
    public static void main(String[] args) {
        int[] arr = {1, 1, 1, 2, 2, 3};
        System.out.println(topKFrequent(arr, 2));
        System.out.println(topKFrequent2(arr, 2));
    }

    /**
     * 优先队列
     * @param nums 数组
     * @param k    topK
     * @return
     */
    public static List<Integer> topKFrequent(int[] nums, int k) {
        //先构造频率映射,然后遍历映射
        //把它们放到堆里,O(n)
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) map.put(num, map.getOrDefault(num, 0) + 1);
        //构建堆,O(logn)
        Queue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>((o1, o2) -> o2.getValue() - o1.getValue());
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) heap.offer(entry);
        List<Integer> res = new ArrayList<>();
        while (k-- > 0) res.add(heap.poll().getKey());
        return res;
    }

    /**
     * 桶排序
     * @param nums 数组
     * @param k    topK
     * @return
     */
    public static List<Integer> topKFrequent2(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            if (map.containsKey(num)) map.put(num, map.get(num) + 1);
            else map.put(num, 1);
        }
        // 创建 nums.length + 1 个桶
        List<Integer>[] bucket = new List[nums.length + 1];
        // 遍历map,根据value值 出现的次数 放到对应的桶中
        for (Map.Entry<Integer, Integer> e : map.entrySet()) {
            Integer value = e.getValue();
            if (bucket[value] == null) bucket[value] = new ArrayList<>();
            bucket[value].add(e.getKey());
        }
        List<Integer> freList = new ArrayList<>();
        // 桶的编号表示出现次数,所以倒数桶
        for (int j = bucket.length - 1; j > -1 && freList.size() < k; j--) {
            if (bucket[j] != null) freList.addAll(bucket[j]);
        }
        return freList;
    }
}

 

寻找数组的中心索引

给定一个整数类型的数组 `nums` ,请编写一个能够返回数组 “中心索引” 的方法。

我们是这样定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。

如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。

示例 1:

输入:

nums = [1, 7, 3, 6, 5, 6]

输出: 3

解释:

索引3 (nums[3] = 6) 的左侧数之和(1 + 7 + 3 = 11),与右侧数之和(5 + 6 = 11)相等。

同时, 3 也是第一个符合要求的中心索引。

示例 2:

输入:

nums = [1, 2, 3]

输出: -1

解释:

数组中不存在满足此条件的中心索引。

说明:

*   `nums` 的长度范围为 `[0, 10000]` 。

*   任何一个 `nums[i]` 将会是一个范围在 `[-1000, 1000]` 的整数。

public static class Solution2 {
    public int pivotIndex(int[] nums) {
        int total = 0;
        for (int num : nums) total += num;        
        int sum = 0;
        for (int i = 0; i < nums.length; sum += nums[i++]) {
            if (sum * 2 == total - nums[i]) return i;            
        }
        return -1;
    }
}

附:大数据常考排序算法——快排和归排(MR中Shuffle和Reduce阶段)以及可能会被问到的堆排

import java.util.Arrays;
import java.util.Random;

public class BaiduBigDataSort {
    public static void main(String[] args) {
        int[] a = {49, 38, 65, 97, 76, 13, 27, 50};
        int[] b1 = {49, 38, 65, 97, 76, 13, 27, 50};
        int[] b2 = {49, 38, 65, 97, 76, 13, 27, 50};
        int[] c = {49, 38, 65, 97, 76, 13, 27, 50};
        MergeSort.mergeSort(a, 0, a.length - 1);
        QuickSort.quickSort(b1, 0, b1.length - 1);
        QuickSort.newQuickSort(b2);
        HeapSort.heapSort(c);
        System.out.println("归排排好序的数组:" + Arrays.toString(a));
        System.out.println("快排排好序的数组:" + Arrays.toString(b1));
        System.out.println("快排优化后排好序的数组:" + Arrays.toString(b2));
        System.out.println("堆排排好序的数组:" + Arrays.toString(c));
    }
}

class MergeSort {
    //两路归并算法,两个排好序的子序列合并为一个子序列
    private static void merge(int[] a, int left, int mid, int right) {
        int[] tmp = new int[a.length];//辅助数组
        int p1 = left, p2 = mid + 1, k = left;//p1、p2是检测指针,k是存放指针
        while (p1 <= mid && p2 <= right) {
            if (a[p1] <= a[p2]) tmp[k++] = a[p1++];
            else tmp[k++] = a[p2++];
        }
        while (p1 <= mid) tmp[k++] = a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
        while (p2 <= right) tmp[k++] = a[p2++];//同上
        for (int i = left; i <= right; i++) a[i] = tmp[i];//复制回原素组
    }

    static void mergeSort(int[] a, int start, int end) {
        if (start < end) {//当子序列中只有一个元素时结束递归
            int mid = (start + end) / 2;//划分子序列
            mergeSort(a, start, mid);//对左侧子序列进行递归排序
            mergeSort(a, mid + 1, end);//对右侧子序列进行递归排序
            merge(a, start, mid, end);//合并
        }
    }
}

class QuickSort {
    //优化前的快排:最好平均最差时间复杂度为O(nlogn)、O(nlogn)、O(n2)
    static void quickSort(int[] arr, int start, int end) {
        int i, j, temp, t;
        if (start > end) return;
        i = start;
        j = end;
        temp = arr[start];//temp就是基准位
        while (i < j) {
            while (temp <= arr[j] && i < j) j--;//先看右边,依次往左递减
            while (temp >= arr[i] && i < j) i++;//再看左边,依次往右递增
            t = arr[j];//如果满足条件则交换
            arr[j] = arr[i];
            arr[i] = t;
        }
        arr[start] = arr[i];//最后将基准为与i和j相等位置的数字交换
        arr[i] = temp;
        quickSort(arr, start, j - 1);//递归调用左半数组
        quickSort(arr, j + 1, end);//递归调用右半数组
    }
    //优化后的快排:最好平均最差时间复杂度均为O(nlogn)
    static void newQuickSort(int[] arr) {
        int random = new Random().nextInt(arr.length + 1);
        int temp = arr[random];//将基准设置为随机元素
        arr[random] = arr[0];
        arr[0] = temp;
        quickSort(arr, 0, arr.length - 1);
    }
}

class HeapSort {
    static void heapSort(int[] arr) {
        //创建堆,从第一个非叶子结点从下至上,从右至左调整结构
        for (int i = (arr.length - 1) / 2; i >= 0; i--) adjustHeap(arr, i, arr.length);
        //调整堆结构+交换堆顶元素与末尾元素
        for (int i = arr.length - 1; i > 0; i--) {
            int temp = arr[i];//将堆顶元素与末尾元素进行交换
            arr[i] = arr[0];
            arr[0] = temp;
            adjustHeap(arr, 0, i);//重新对堆进行调整
        }
    }

    //调整堆@param arr待排序列 @param parent 父节点 @param length 待排序列尾元素索引
    private static void adjustHeap(int[] arr, int parent, int length) {
        int temp = arr[parent];//将temp作为父节点
        int lChild = 2 * parent + 1;//左孩子
        while (lChild < length) {
            int rChild = lChild + 1;//右孩子
            if (rChild < length && arr[lChild] < arr[rChild]) lChild++;// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
            if (temp >= arr[lChild]) break;// 如果父结点的值已经大于孩子结点的值,则直接结束
            arr[parent] = arr[lChild];// 把孩子结点的值赋给父结点
            parent = lChild;//选取孩子结点的左孩子结点,继续向下筛选
            lChild = 2 * lChild + 1;
        }
        arr[parent] = temp;
    }
}

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值