- 1.最长公共前缀
- 2.三数之和
- 3.电话号码的字母组合
- 4.删除链表的倒数第 N 个结点
- 5.有效的括号
- 6.合并两个有序链表
- 7.括号生成
- 8.合并K个升序链表
- 9.删除有序数组中的重复项
- 10.实现 strStr()
编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”。
解题思路:纵向扫描法,使用第一个字符串的每一个字符与其它所有的字符串依次比较。
public String longestCommonPrefix(String[] strs) {
if(strs == null || strs.length == 0){
return "";
}
int length = 0;
int i,j;
for(i = 0; i< strs[0].length(); i++){
// 用strs[0]的每一个字符,与其它字符串的每一位比较
for(j = 1;j= strs[j].length() || strs[j].charAt(i) != strs[0].charAt(i)){
return strs[0].substring(0,length);
}
}
length++;
}
return strs[0];
}
2.三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
解题思路:暴力解法,找出 a b c,然后排序,去重。
然后超时了,时间复杂度为 O(n3)
public List> threeSum(int[] nums) { Set result = new HashSet(); for(int i = 0;i < nums.length;i++){ for(int j = 0;j < nums.length;j++){ if(j != i) { for (int k = 0; k < nums.length; k++) { if(k != j && k != i) { if (nums[i] + nums[j] + nums[k] == 0) { List list = new ArrayList(); list.add(nums[i]); list.add(nums[j]); list.add(nums[k]); Collections.sort(list); result.add(list); } } } } } } return new ArrayList<>(result); }
方法二:排序 + 双指针
算法思想:固定一个值,求两数之和为固定值的负值,这样两数之和与固定值的和就是0。关键在于去重。先排序,将值从小到大排列,在遍历的时候,跳过重复的值。
public List3.电话号码的字母组合> threeSum(int[] nums) { // 排序,从小到大 Arrays.sort(nums); int target; int L,R,value; List
> results = new ArrayList<>(); // 每次固定一个 nums[i] 值 for(int i=0; i
0 && nums[i] == nums[i - 1]){ continue; } // 两数之和的目标值为固定值的负值。 target = -nums[i]; L = i + 1; R = nums.length - 1; // 从 i值的后面的所有元素查找 while(R > L){ value = nums[R] + nums[L]; // 两数之和等于目标值 if(value == target){ ArrayList result = new ArrayList(); result.add(nums[i]); result.add(nums[R]); result.add(nums[L]); // 放入结果集合 results.add(result); // 继续寻找,排除重复的值 while(R > L && nums[R - 1] == nums[R]){ R--; } R--; // 继续寻找,排除重复的值 while(R > L && nums[L + 1] == nums[L]){ L++; } L++; }else if(value < target){ // 两数之和小于,则L指针右移。 L++; }else{ // 两数之和大于,则R指针左移。 R--; } } } return results; }
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。
2: abc
3: def
4:ghi
5:jkl
6:mno
7:pqrs
8:tuv
9:wxyz
解题思路:使用深度优先,遍历得到所有的组合。
public ListletterCombinations(String digits) { // 保存结果 List result = new ArrayList<>(); if(digits.length() == 0){ // 为零直接返回空 return result; } // 建立映射表 HashMap map = new HashMap (); map.put('2', "abc"); map.put('3', "def"); map.put('4', "ghi"); map.put('5', "jkl"); map.put('6', "mno"); map.put('7', "pqrs"); map.put('8', "tuv"); map.put('9', "wxyz"); // 深度优先遍历 DFS(digits,0,map,result,new StringBuffer()); return result; } // 参数说明 // digits存储数字的字符串,index为数字串的下标,map为 映射表,result为结果,t保持中间的结果 public void DFS(String digits, int index,Map map,List result,StringBuffer t) { if(index == digits.length()){ // 深度优先到最深层,将中间结果t存入最终结果result中。 result.add(t.toString()); return; } // 得到数字对应的字母 String s = (String) map.get(digits.charAt(index)); for(int i = 0;i 4.删除链表的倒数第 N 个结点 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
方法一: 递归到链表尾部,然后逐层返回并计数,直到找到倒数第n个节点,返回。static int t; public static ListNode removeNthFromEnd(ListNode head, int n){ if(head.next == null){ t = 1; if(n == t){ head = null; } return head; } head.next = removeNthFromEnd(head.next,n); t++; if(t == n){ return head.next; } return head; }方法二:快慢指针,快指针比满指针快 n 个节点。这样当快指针到达链表尾部。慢指针恰好指向第n个节点。
public static ListNode removeNthFromEnd(ListNode head, int n){ ListNode dummy = new ListNode(0, head); ListNode first = head; ListNode second = dummy; for(int i = 0; i < n;i++){ first = first.next; } while(first != null){ first = first.next; second = second.next; } second.next = second.next.next; return dummy.next; }5.有效的括号给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
解题思路:使用栈来解题,左括号入栈,右括号出栈。
public boolean isValid(String s) { if(s.length()%2 == 1){ return false; } Stack6.合并两个有序链表stack = new Stack<>(); HashMap map = new HashMap<>(); map.put('(',')'); map.put('[',']'); map.put('{','}'); Character c; for(int i = 0;i < s.length();i++){ c = s.charAt(i); if(map.containsKey(c)){ stack.push(c); }else{ if(stack.empty() || c != map.get(stack.pop())){ return false; } } } return stack.isEmpty(); } 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
解题思路:没啥说的,干。public ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode result = new ListNode(); ListNode p = result; while(list1 != null && list2 != null) { if (list1.val <= list2.val) { p.next = list1; list1 = list1.next; } else { p.next = list2; list2 = list2.next; } p = p.next; } p.next = list1 == null?list2:list1; return result.next; }7.括号生成数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:输入:n = 3 输出:["((()))","(()())","(())()","()(())","()()()"] 输入:n = 1 输出:["()"]解题思路:回溯剪枝,剪枝条件:左括号的个数大于0,右括号的个数要小于左括号的个数才能放入。
如图:n为2的情况下的所有可能,进行剪枝操作。
public List8.合并K个升序链表generateParenthesis(int n) { // 回溯剪枝 List results = new ArrayList<>(); DFS(n,n,results,new StringBuffer()); return results; } public void DFS(int left,int right,List results,StringBuffer t){ if(left == 0 && right == 0){ results.add(t.toString()); return; } if(left > 0){ t.append("("); DFS(left-1,right,results,t); t.deleteCharAt(t.length()-1); } if( right > left){ t.append(")"); DFS(left,right-1,results,t); t.deleteCharAt(t.length()-1); } } 给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6解题思路:分治算法,将大的问题转换成若干个子问题。
public ListNode mergeKLists(ListNode[] lists) { if(lists.length == 0){ return null; } if(lists.length == 1){ return lists[0]; } ListNode list; list = divideAndConquer(lists,0,lists.length - 1); return list; } public ListNode divideAndConquer(ListNode[] lists,int start, int end){ ListNode merge1 = null; ListNode merge2 = null; if(end - start > 1){ int mid = start + (end-start)/2; merge1 = divideAndConquer(lists,start, mid); merge2 = divideAndConquer(lists,mid + 1,end); }else{ if(end == start){ merge1 = lists[start]; }else{ merge1 = mergeTwoLists(lists[start],lists[end]); } } return mergeTwoLists(merge1,merge2); } public ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode result = new ListNode(); ListNode p = result; while(list1 != null && list2 != null) { if (list1.val <= list2.val) { p.next = list1; list1 = list1.next; } else { p.next = list2; list2 = list2.next; } p = p.next; } p.next = list1 == null?list2:list1; return result.next; }9.删除有序数组中的重复项给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。
解题思路:使用快慢指针,快指针将第一次遇见的数,赋值给慢指针指向的位置。等待快指针遍历完整个数组,就知道数组中有多少不同的值。public int removeDuplicates(int[] nums) { if(nums.length == 0){ return 0; } if(nums.length == 1){ return 1; } int first = 1,second = 1; while(first < nums.length){ if(nums[first] != nums[first - 1]) { nums[second] = nums[first]; ++second; } first++; } return second; }10.实现 strStr()实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
解题思路:KMP算法
next数组中的值就是子串的最大相同前缀和后缀的个数
参考视频public int strStr(String haystack, String needle) { int needleLength = needle.length(); // 当needle是空字符串时,返回0 if (needleLength == 0) return 0; // 定义好next数组 int[] next = new int[needleLength]; // 定义好两个指针right与left // 在for循环中初始化指针right为1,left=0,开始计算next数组,right始终在left指针的后面 for (int left = 0,right = 1; right < needleLength; right++) { // 如果不相等就让left指针回退,到0时就停止回退 while (left > 0 && needle.charAt(left) != needle.charAt(right)) { //进行回退操作; left = next[left - 1]; } if (needle.charAt(left) == needle.charAt(right)) { next[right] = ++left; }else{ next[right] = 0; } } // 循环结束的时候,next数组就已经计算完毕了 // 根据next数组进行串的匹配 for (int i = 0,j=0; i0 && haystack.charAt(i) != needle.charAt(j)){ j=next[j-1]; } if (haystack.charAt(i)==needle.charAt(j)){ j++; } if (j == needleLength) { return i - needleLength + 1; } } return -1; }



