1. 兩數之和
給定一個整數數組 nums
和一個目標值 target
,請你在該數組中找出和為目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重復利用這個數組中同樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9 因為 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
/**
* 暴力枚舉法
* @param nums
* @param target
* @return
*/
public static int[] twoSum(int[] nums, int target) {
int lgn = nums.length;
for(int i = 0; i < lgn; i++){
for(int j = i + 1; j < lgn; j++){
if(nums[i] + nums[j] == target){
return new int[]{i, j};
}
}
}
return new int[0];
}
/**
* MAP 處理
* @param nums
* @param target
* @return
*/
public static int[] twoSum1(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { complement, nums[i] };
}
map.put(nums[i], i);
}
return new int[0];
}
2. 兩數相加
給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,並且它們的每個節點只能存儲 一位 數字。
如果,我們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。
您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。
示例:
輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 輸出:7 -> 0 -> 8 原因:342 + 465 = 807
/**
* 鏈表相應位置依次相加,最后處理進位
* @param l1
* @param l2
* @return
*/
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null;
ListNode curr = null;
while (l1 != null || l2 != null){
if(l1 != null && l2 != null){
ListNode node = new ListNode(l1.val + l2.val);
if(curr == null && head == null) head = node;
curr = initNode(curr, node);
}else{
curr = initNode(curr, new ListNode(l1 != null?l1.val:l2.val));
}
l1 = l1 != null?l1.next:null;
l2 = l2 != null?l2.next:null;
}
curr = head;
while (curr != null){
if(curr.val >= 10){
curr.val -= 10;
if(curr.next == null){
curr.next = new ListNode(1);
}else {
curr.next.val += 1;
}
}
curr = curr.next;
}
curr = null;
return head;
}
public ListNode initNode(ListNode curr, ListNode newNode){
if(curr != null){
curr.next = newNode;
}
curr = newNode;
return curr;
}
3. 尋找兩個有序數組的中位數
給定兩個大小為 m 和 n 的有序數組 nums1
和 nums2
。
請你找出這兩個有序數組的中位數,並且要求算法的時間復雜度為 O(log(m + n))。
你可以假設 nums1
和 nums2
不會同時為空。
示例 1:
nums1 = [1, 3] nums2 = [2] 則中位數是 2.0
示例 2:
nums1 = [1, 2] nums2 = [3, 4] 則中位數是 (2 + 3)/2 = 2.5
/**
* 使用兩個排序數據組的歸並過程
* 分別定義兩個數組的遍歷索引,每次對比提取相應數組的元素
* 不實際存儲歸並后的數據,
* 處理前半數元素即可
* @param nums1
* @param nums2
* @return
*/
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
int lgn1 = nums1.length;
int lgn2 = nums2.length;
int allLgn = lgn1 + lgn2;
int middleIndex = allLgn/2;
int middleLeft = 0,middleRight = 0;
int index1 = 0;
int index2 = 0;
int curr = 0;
for (int i = 0; i < middleIndex + 1; i++) {
if(index1 < lgn1 && index2 < lgn2) {
if (nums1[index1] > nums2[index2]) {
curr = nums2[index2];
index2++;
} else {
curr = nums1[index1];
index1++;
}
}else if(index1 < lgn1){
curr = nums1[index1];
index1++;
}else if(index2 < lgn2){
curr = nums2[index2];
index2++;
}
if(i == middleIndex - 1){
middleLeft = curr;
}
if(i == middleIndex){
middleRight = curr;
}
}
if(allLgn%2 == 0){
return (middleLeft + middleRight)/2.0;
}else {
return middleRight;
}
}
4. Z 字形變換
將一個給定字符串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。
比如輸入字符串為 "LEETCODEISHIRING"
行數為 3 時,排列如下:
L C I R E T O E S I I G E D H N
之后,你的輸出需要從左往右逐行讀取,產生出一個新的字符串,比如:"LCIRETOESIIGEDHN"
。
請你實現這個將字符串進行指定行數變換的函數:
string convert(string s, int numRows);
示例 1:
輸入: s = "LEETCODEISHIRING", numRows = 3 輸出: "LCIRETOESIIGEDHN"
示例 2:
輸入: s = "LEETCODEISHIRING", numRows = 4 輸出: "LDREOEIIECIHNTSG" 解釋: L D R E O E I I E C I H N T S G
/**
* 定義目標行數個鏈表,如示例,每次中間間隔的 numRows - 2 個列表逐個填充一個值
* 便利給定的字符串,依次處理,直到末尾
* @param s
* @param numRows
* @return
*/
public static String convert(String s, int numRows) {
if(numRows == 1){
return s;
}
String result = "";
if(numRows == 2){
result = "";
for (int i = 0; i < s.length(); i = i + 2) {
result += s.charAt(i);
}
for (int i = 1; i < s.length(); i = i + 2) {
result += s.charAt(i);
}
return result;
}
int middleCount = numRows - 2;
List[] all = new LinkedList[numRows];
for (int i = 0; i < numRows; i++) {
all[i] = new LinkedList<>();
}
int sIndex = 0;
int step = 0;
while (sIndex < s.length()){
for (int i = 0; i < numRows; i++) {
if(sIndex == s.length()) break;
all[i].add(s.charAt(sIndex));
sIndex++;
}
for (int j = numRows - 2; j > 0 ; j--) {
if(sIndex == s.length()) break;
all[j].add(s.charAt(sIndex));
sIndex++;
}
step = step + middleCount;
}
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < all[i].size(); j++) {
result += all[i].get(j);
}
}
return result;
}
5. 整數反轉
給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
示例 1:
輸入: 123 輸出: 321
示例 2:
輸入: -123 輸出: -321
示例 3:
輸入: 120 輸出: 21
注意:
假設我們的環境只能存儲得下 32 位的有符號整數,則其數值范圍為 [−231, 231 − 1]。請根據這個假設,如果反轉后整數溢出那么就返回 0。
/**
* 數據范圍判斷
* @param x
* @return
*/
public static int reverse(int x) {
double result = 0;
while (x != 0){
result = result * 10 + x%10;
if (result > Integer.MAX_VALUE) return 0;
if (result < Integer.MIN_VALUE) return 0;
x = x/10;
}
return (int) result;
}
6. 回文數
判斷一個整數是否是回文數。回文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數。
/**
* 轉化為字符串,一次便利,首末對稱位置對比
* @param x
* @return
*/
public static boolean isPalindrome(int x) {
String s = String.valueOf(x);
int lgn = s.length();
for (int i = 0,j = lgn -1; i <= j; i++,j--){
if(s.charAt(i) == s.charAt(j)){
continue;
}else {
return false;
}
}
return true;
}
7. 盛最多水的容器
給定 n 個非負整數 a1,a2,...,an,每個數代表坐標中的一個點 (i, ai) 。在坐標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0)。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。
說明:你不能傾斜容器,且 n 的值至少為 2。
/**
* 枚舉
* @param height
* @return
*/
public static int maxArea(int[] height) {
int area = 0, lgn = height.length;
if(lgn < 2) return 0;
for (int i = 0; i < lgn; i++) {
for (int i1 = i + 1; i1 < lgn; i1++) {
int tmpArea = (height[i]>height[i1]?height[i1]:height[i]) * (i1 - i);
if(tmpArea > area){
area = tmpArea;
}
}
}
return area;
}
/**
* 雙指針
* @param height
* @return
*/
public static int maxArea2(int[] height) {
int area = 0, lgn = height.length;
if(lgn < 2) return 0;
for (int i = 0, j = lgn - 1; i < j; ) {
int tmpArea = (height[i]>height[j]?height[j]:height[i]) * Math.abs(j - i);
if(tmpArea > area){
area = tmpArea;
i++;//正方向前進一步,避免反方向遍歷時,重復比較
}else {
j--;//反方向前進一步,避免正方向遍歷時,重復比較
}
}
return area;
}
8. 整數轉羅馬數字
羅馬數字包含以下七種字符: I
, V
, X
, L
,C
,D
和 M
。
字符 數值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000
例如, 羅馬數字 2 寫做 II
,即為兩個並列的 1。12 寫做 XII
,即為 X
+ II
。 27 寫做 XXVII
, 即為 XX
+ V
+ II
。
通常情況下,羅馬數字中小的數字在大的數字的右邊。但也存在特例,例如 4 不寫做 IIII
,而是 IV
。數字 1 在數字 5 的左邊,所表示的數等於大數 5 減小數 1 得到的數值 4 。同樣地,數字 9 表示為 IX
。這個特殊的規則只適用於以下六種情況:
I
可以放在V
(5) 和X
(10) 的左邊,來表示 4 和 9。X
可以放在L
(50) 和C
(100) 的左邊,來表示 40 和 90。C
可以放在D
(500) 和M
(1000) 的左邊,來表示 400 和 900。
給定一個整數,將其轉為羅馬數字。輸入確保在 1 到 3999 的范圍內。
public static String intToRoman(int num) {
if(num > 3999) return "";
if(num/1000 > 0){
return dealQianWei(num);
}else if(num/100 > 0){
return dealBaiWei(num);
}else if(num/10 > 0){
return dealShiWei(num);
}else {
return dealGeWei(num);
}
}
/**
* 千位
* @param num
* @return
*/
public static String dealQianWei(int num){
return countStr(num/1000, "M") + dealBaiWei(num%1000);
}
/**
* 百位
* @param num
* @return
*/
public static String dealBaiWei(int num){
int bc = num/100;
if(bc == 9) return "CM" + dealShiWei(num % 100);
if(bc == 4) return "CD" + dealShiWei(num % 100);
int fbc = num/500;
num = num%500;
return countStr(fbc, "D") + countStr(num/100, "C") + dealShiWei(num%100);
}
/**
* 十位
* @param num
* @return
*/
public static String dealShiWei(int num){
int tens = num/10;
if(tens == 9) return "XC" + dealGeWei(num % 10);
if(tens == 4) return "XL" + dealGeWei(num % 10);
int ftens = num/50;
num = num%50;
return countStr(ftens, "L") + countStr(num/10, "X") + dealGeWei(num%10);
}
/**
* 個位
* @param num
* @return
*/
public static String dealGeWei(int num){
if(num == 9) return "IX";
if(num == 4) return "IV";
if(num >= 5) return "V" + dealGeWei(num % 5);
return countStr(num, "I");
}
public static String countStr(int count, String num){
if(count == 0) return "";
String result = "";
for (int i = 0; i < count; i++) {
result += num;
}
return result;
}
9. 三數之和
給定一個包含 n 個整數的數組 nums
,判斷 nums
中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重復的三元組。
注意:答案中不可以包含重復的三元組。
注意:利用上面的兩數值和
public static List<List<Integer>> threeSum(int[] nums) {
if(nums == null || nums.length < 3) return new ArrayList();
Set<List<Integer>> result = new HashSet<>();
List<Integer> numList = new ArrayList();
for (int num : nums) {
numList.add(num);
}
for (Integer num : numList) {
List<Integer> copy = new ArrayList();
copy.addAll(numList);
copy.remove(num);
List<int[]> tmp = twoSum(copy, -num);
if(tmp.size()>0){
for (int[] ints : tmp) {
List<Integer> list = new ArrayList(){{add(num);add(ints[0]);add(ints[1]);}};
Collections.sort(list);
result.add(list);
}
}
}
return new ArrayList(result);
}
public static List<int[]> twoSum(List<Integer> nums, int target) {
List<int[]> result = new ArrayList();
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums.get(i);
if (map.containsKey(complement)) {
result.add(new int[] { complement, nums.get(i) });
}
map.put(nums.get(i), i);
}
return result;
}
10. 最接近的三數之和
給定一個包括 n 個整數的數組 nums
和 一個目標值 target
。找出 nums
中的三個整數,使得它們的和與 target
最接近。返回這三個數的和。假定每組輸入只存在唯一答案。
例如,給定數組 nums = [-1,2,1,-4], 和 target = 1. 與 target 最接近的三個數的和為 2. (-1 + 2 + 1 = 2).
public static int threeSumClosest(int[] nums, int target) {
int min = Integer.MAX_VALUE;
int ele1 = 0, ele2 = 0, ele3 = 0;
for (int i = 0; i < nums.length; i++) {
for (int i1 = 0; i1 < nums.length; i1++) {
if (i1 == i) continue;
for (int i2 = 0; i2 < nums.length; i2++) {
if (i2 == i1 || i2 == i) continue;
int sum = Math.abs(nums[i] + nums[i1] + nums[i2] - target);
if (sum < min) {
min = sum;
ele1 = nums[i];
ele2 = nums[i1];
ele3 = nums[i2];
}
}
}
}
return ele1 + ele2 + ele3;
}
11. 缺失的第一個正數
給定一個未排序的整數數組,找出其中沒有出現的最小的正整數。
示例 1:
輸入: [1,2,0] 輸出: 3
示例 2:
輸入: [3,4,-1,1] 輸出: 2
示例 3:
輸入: [7,8,9,11,12] 輸出: 1
/**
* 數組操作
* @param nums
* @return
*/
public static int firstMissingPositive(int[] nums) {
int max = 0;
for (int i = 0; i < nums.length; i++) {
if(nums[i] < 0) continue;
if(nums[i] > max){
max = nums[i];
}
}
max = max == Integer.MAX_VALUE?max:max + 2;
for (int i = 1; i < max; i++) {
if(contains(nums, i)) continue;
return i;
}
return max + 1;
}
public static boolean contains(int[] nums, int ele){
for (int i = 0; i < nums.length; i++) {
if(nums[i] == ele) return true;
}
return false;
}
/**
* map操作
* @param nums
* @return
*/
public static int firstMissingPositive1(int[] nums) {
Map<Integer, Integer> vs = new HashMap<>();
int max = 0;
for (int i = 0; i < nums.length; i++) {
if(nums[i] < 0) continue;
if(nums[i] > max){
max = nums[i];
}
vs.put(nums[i], i);
}
max = max == Integer.MAX_VALUE?max:max + 2;
for (int i = 1; i < max; i++) {
if(vs.get(i) != null) continue;
return i;
}
return max + 1;
}
12. 接雨水
給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之后能接多少雨水。
上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)。 感謝 Marcos 貢獻此圖。
示例:
輸入: [0,1,0,2,1,0,1,3,2,1,2,1] 輸出: 6
/**
* 分割
* @param height
* @return
*/
public static int trap(int[] height) {
//最大索引位置
int maxIndex = findMax(height);
int lsubMaxindex = maxIndex, rsubMaxIndex = maxIndex;
int area = 0;
//左邊處理
while (lsubMaxindex > 0){
int tmpMax = lsubMaxindex;
lsubMaxindex = findMax(Arrays.copyOfRange(height, 0, tmpMax));
area += height[lsubMaxindex] * (tmpMax - lsubMaxindex - 1);
for (int i = lsubMaxindex + 1; i < tmpMax; i++) {
area -= height[i] * 1;
}
}
//右邊處理
while (rsubMaxIndex < height.length - 1){
int tmpMax = rsubMaxIndex;
rsubMaxIndex = tmpMax + findMax(Arrays.copyOfRange(height, tmpMax + 1, height.length)) + 1;
area += height[rsubMaxIndex] * (rsubMaxIndex - tmpMax - 1);
for (int i = tmpMax + 1; i < rsubMaxIndex; i++) {
area -= height[i] * 1;
}
}
return area;
}
public static int findMax(int[] nums){
int max = 0, maxIndex = 0;
for (int i = 0; i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
maxIndex = i;
}
}
return maxIndex;
}
13. 字符串相乘
給定兩個以字符串形式表示的非負整數 num1
和 num2
,返回 num1
和 num2
的乘積,它們的乘積也表示為字符串形式。
示例 1:
輸入: num1 = "2", num2 = "3" 輸出: "6"
示例 2:
輸入: num1 = "123", num2 = "456" 輸出: "56088"
說明:
num1
和num2
的長度小於110。num1
和num2
只包含數字0-9
。num1
和num2
均不以零開頭,除非是數字 0 本身。- 不能使用任何標准庫的大數類型(比如 BigInteger)或直接將輸入轉換為整數來處理。
public static String multiply(String num1, String num2) {
if(num1 == null || num2 == null || "".equals(num1) || "".equals(num2) || "0".equals(num1) || "0".equals(num2)) return String.valueOf(0);
int lgn1 = num1.length(), lgn2 = num2.length();
int[] result = new int[lgn1 + lgn2];
int resultIndex = result.length - 1;
for (int i = lgn1 - 1; i > -1 ; i--) {
int first = Integer.parseInt(String.valueOf(num1.charAt(i)));
int innerIndex = 0;
for (int j = lgn2 - 1; j > -1 ; j--) {
int second = Integer.parseInt(String.valueOf(num2.charAt(j)));
int plus = first * second;
result[resultIndex - innerIndex] += plus%10;
if(plus >= 10) {
result[resultIndex - innerIndex - 1] += plus / 10;
}
innerIndex++;
}
resultIndex--;
}
//處理進位
StringBuilder sb = new StringBuilder();
for (int i = result.length - 1; i >= 0; i--) {
if(result[i]>=10) {
result[i - 1] += result[i]/10;
result[i] %= 10;
}
}
//提取有效位
boolean start = false;
for (int i = 0; i < lgn1 + lgn2 ; i++) {
if(!start && result[i] != 0){
start = true;
}
if(start){
sb.append(result[i]);
}
}
return sb.toString();
}
14. 跳躍游戲 II
給定一個非負整數數組,你最初位於數組的第一個位置。
數組中的每個元素代表你在該位置可以跳躍的最大長度。
你的目標是使用最少的跳躍次數到達數組的最后一個位置。
示例:
輸入: [2,3,1,1,4] 輸出: 2 解釋: 跳到最后一個位置的最小跳躍數是2
。 從下標為 0 跳到下標為 1 的位置,跳1
步,然后跳3
步到達數組的最后一個位置。
說明:
假設你總是可以到達數組的最后一個位置。
/**
* 枚舉遍歷
* @param nums
* @return
*/
public static int jump(int[] nums) {
if(nums == null || nums.length == 1) return 0;
if(nums.length == 2) return 1;
int steps = Integer.MAX_VALUE/2;
int initStep = 1;
if(nums[0] >= nums.length - 1) return 1;
if(nums[0] == 0) return steps;
while (initStep <= nums[0]){
int subNeedStep = jump(Arrays.copyOfRange(nums, initStep, nums.length));
if(subNeedStep + 1 < steps){
steps = subNeedStep + 1;
}
initStep++;
}
return steps;
}
/**
* 每次選擇 和下一跳(最大跳值)之和最遠的=》遞歸處理
* @param nums
* @return
*/
public static int jump2(int[] nums) {
if(nums == null || nums.length == 1) return 0;
if(nums.length == 2) return 1;
int steps = Integer.MAX_VALUE/2;
int initStep = nums[0];
if(nums[0] >= nums.length - 1) return 1;
if(nums[0] == 0) return steps;
int maxSum = 0;
int fromStep = 1;
while (initStep > 0){
if(initStep + nums[initStep] > maxSum){
fromStep = initStep;
maxSum = initStep + nums[initStep];
}
initStep--;
}
steps = 1 + jump2(Arrays.copyOfRange(nums, fromStep, nums.length));
return steps;
}
15. 全排列
給定一個沒有重復數字的序列,返回其所有可能的全排列。
示例:
輸入: [1,2,3] 輸出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
/**
* 遞歸處理
* @param nums
* @return
*/
public static List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList();
if(nums.length == 1) {
result.add(new ArrayList(){{add(nums[0]);}});
return result;
}
for (int num : nums) {
int[] tmp = new int[nums.length - 1];
int index = 0;
for (int i = 0; i < nums.length; i++) {
if(num == nums[i]) continue;
tmp[index] = nums[i];
index++;
}
List<List<Integer>> sub = permute(tmp);
sub.stream().forEach(item->item.add(num));
result.addAll(sub);
}
return result;
}
給定一個可包含重復數字的序列,返回所有不重復的全排列。
示例:
輸入: [1,1,2] 輸出: [ [1,1,2], [1,2,1], [2,1,1] ]
/**
* 遞歸處理
* Set 處理重復
* @param nums
* @return
*/
public static List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> result = new ArrayList();
if(nums.length == 1) {
result.add(new ArrayList(){{add(nums[0]);}});
return result;
}
for (int num : nums) {
int[] tmp = new int[nums.length - 1];
int index = 0;
for (int i = 0; i < nums.length; i++) {
if(num == nums[i] && index == i) continue;
tmp[index] = nums[i];
index++;
}
List<List<Integer>> sub = permuteUnique(tmp);
sub.stream().forEach(item->item.add(num));
result.addAll(sub);
}
Set<List<Integer>> sets = new HashSet();
result.stream().forEach(item->sets.add(item));
return new ArrayList(sets);
}
16. 旋轉圖像
給定一個 n × n 的二維矩陣表示一個圖像。
將圖像順時針旋轉 90 度。
說明:
你必須在原地旋轉圖像,這意味着你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉圖像。
示例 1:
給定 matrix = [ [1,2,3], [4,5,6], [7,8,9] ], 原地旋轉輸入矩陣,使其變為: [ [7,4,1], [8,5,2], [9,6,3] ]
public static void rotate(int[][] matrix) {
int step = matrix.length;
int[][] tmp = new int[step][step];
for (int i = 0; i < step; i++) {
for (int j = 0; j < step; j++) {
tmp[i][j] = matrix[step - j - 1][i];
}
}
for (int i = 0; i < step; i++) {
for (int j = 0; j < step; j++) {
matrix[i][j] = tmp[i][j];
}
}
}
17. 字母異位詞分組
給定一個字符串數組,將字母異位詞組合在一起。字母異位詞指字母相同,但排列不同的字符串。
示例:
輸入: ["eat", "tea", "tan", "ate", "nat", "bat"]
,
輸出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
說明:
- 所有輸入均為小寫字母。
- 不考慮答案輸出的順序。
public static List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> result = new ArrayList();
if(strs == null ||strs.length == 0) return result;
if(strs.length == 1){
result.add(Arrays.asList(strs[0]));
return result;
}
Map<String, List<String>> maps = new HashMap();
for (String str : strs) {
char[] arr = str.toCharArray();
Arrays.sort(arr);
String sorted = Arrays.toString(arr);
if(maps.get(sorted) != null){
maps.get(sorted).add(str);
}else {
maps.put(sorted, new ArrayList(){{add(str);}});
}
}
maps.remove(null);
return maps.values().stream().collect(Collectors.toList());
}
18. Pow(x, n)
實現 pow(x, n) ,即計算 x 的 n 次冪函數。
示例 1:
輸入: 2.00000, 10 輸出: 1024.00000
示例 2:
輸入: 2.10000, 3 輸出: 9.26100
示例 3:
輸入: 2.00000, -2 輸出: 0.25000 解釋: 2
-2
= 1/2
2
= 1/4 = 0.25
說明:
- -100.0 < x < 100.0
- n 是 32 位有符號整數,其數值范圍是 [−231, 231 − 1] 。
/**
* 快速降冪 避免遞歸過深造成棧溢出
* @param x
* @param n
* @return
*/
public static double power(double x, int n) {
if(!(x > -100 && x < 100)) return 0;
if(!(n <= Integer.MAX_VALUE && n >= Integer.MIN_VALUE)) return 0;
if(x == 0) return 0;
if(x == 1) return 1;
if(n == 0) return 1;
if(n == 1) return x;
if(n == -1) return 1/x;
if(n > 1 || n < -1){
double nextValue = power(x, n / 2);
return (n % 2 == 0 ? 1 : (n > 0 ? x : 1/x)) * nextValue * nextValue;
}
return x;
}
19. 進制轉換
進制轉換 十進制 =》 62進制
這里所謂62進制是指采用0~9A~Za~z等62個字符進行編碼(按ASCII順序由小到大)。
public static String BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static String transfer(int num) {
int scale = 62;
StringBuilder sb = new StringBuilder();
while (num > 0) {
//余數對照進制基本位 BASE 放到相應位置
sb.append(BASE.charAt(num % scale));
//除處理下一進位
num = num / scale;
}
sb.reverse();
return sb.toString();
}
20. 報數
報數序列是一個整數序列,按照其中的整數的順序進行報數,得到下一個數。其前五項如下:
1. 1 2. 11 3. 21 4. 1211 5. 111221
1
被讀作 "one 1"
("一個一"
) , 即 11
。11
被讀作 "two 1s"
("兩個一"
), 即 21
。21
被讀作 "one 2"
, "one 1"
("一個二"
, "一個一"
) , 即 1211
。
給定一個正整數 n(1 ≤ n ≤ 30),輸出報數序列的第 n 項。
注意:整數順序將表示為一個字符串。
public static String countAndSay(int n) {
if(n < 1 || n > 30) return "";
int start = 1;
String report = "1";
String result = "";
while (start < n ){
result = "";
for (int i = 0; i < report.length();) {
int c = i;
int count = 1;
while (c + 1 < report.length()){
if(report.charAt(c) != report.charAt(c + 1)){
break;
}
count++;
c++;
}
result += String.valueOf(count) + String.valueOf(report.charAt(i));
i = i + count;
}
report = result;
start++;
}
return report;
}
21. 最長回文子串
給定一個字符串 s
,找到 s
中最長的回文子串。你可以假設 s
的最大長度為 1000。
示例 1:
輸入: "babad" 輸出: "bab" 注意: "aba" 也是一個有效答案。
示例 2:
輸入: "cbbd" 輸出: "bb"
public static String longestPalindrome(String s) {
if(s == null || s.length() == 1 || s.length() == 0) return s;
if(s.length() == 2) return s.charAt(0) == s.charAt(1)?s:String.valueOf(s.charAt(0));
String maxP = "";
//中間索引
int middle = (s.length()-1)/2;
//字符串長度奇偶 判別
boolean dmiddle = s.length()%2 == 0 && s.charAt(middle) == s.charAt(middle + 1);
//遍歷使用臨時中間字符索引
int tmpMiddle = middle;
//緊鄰元素接次判斷
int initJudge = 1;
//每次判斷中間最長回文字符
String tp = "";
//最開始 判斷是否偶長度
boolean first = true;
//中間字符逐次左移判斷
while (tmpMiddle > 0) {
initJudge = 1;
//左右指針
int lindex = tmpMiddle - 1, rindex = tmpMiddle + (first && dmiddle?2:1);
while (lindex > -1 && rindex < s.length()) {
if(initJudge == 1) {
//左邊緊鄰接次判斷
if (s.charAt(lindex) == s.charAt(tmpMiddle)) {
lindex--;
initJudge++;
}
//右邊緊鄰接次判斷
if (s.charAt(rindex) == s.charAt(tmpMiddle + (first && dmiddle?1:0))) {
rindex++;
initJudge++;
}
initJudge = initJudge>1?1:0;
first = false;
tp = s.substring(lindex + 1, rindex);
continue;
}
//對稱位置判斷
if (s.charAt(lindex) == s.charAt(rindex)) {
lindex--;
rindex++;
tp = s.substring(lindex + 1, rindex);
continue;
}
tp = s.substring(lindex + 1, rindex);
break;
}
if(tp.length() > maxP.length()){
maxP = tp;
}
tmpMiddle--;
}
tmpMiddle = middle;
tp = "";
first = false;
//中間字符逐次右移判斷
while (tmpMiddle < s.length()) {
initJudge = 1;
//左右指針
int lindex = tmpMiddle - 1, rindex = tmpMiddle + (first && dmiddle?2:1);
while (lindex > -1 && rindex < s.length()) {
if(initJudge == 1) {
//左邊緊鄰接次判斷
if (s.charAt(lindex) == s.charAt(tmpMiddle)) {
lindex--;
initJudge++;
}
//右邊緊鄰接次判斷
if (s.charAt(rindex) == s.charAt(tmpMiddle + (first && dmiddle?1:0))) {
rindex++;
initJudge++;
}
initJudge = initJudge>1?1:0;
tp = s.substring(lindex + 1, rindex);
continue;
}
//對稱位置判斷
if (s.charAt(lindex) == s.charAt(rindex)) {
lindex--;
rindex++;
tp = s.substring(lindex + 1, rindex);
continue;
}
tp = s.substring(lindex + 1, rindex);
break;
}
if(tp.length() > maxP.length()){
maxP = tp;
}
tmpMiddle++;
}
return maxP;
}
22. 電話號碼的字母組合
給定一個僅包含數字 2-9
的字符串,返回所有它能表示的字母組合。
給出數字到字母的映射如下(與電話按鍵相同)。注意 1 不對應任何字母。
示例:
輸入:"23" 輸出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
輸入:"234"
輸出:
[aeg, ceg, beh, aei, beg, aeh, cei, ceh, bei, bfg, afh, afg, cfh, bdg, bfi, adh, cfg, bfh, adg, afi, cdh, bdi, cdg, cfi, bdh, adi, cdi].
說明:
盡管上面的答案是按字典序排列的,但是你可以任意選擇答案輸出的順序。
public static Map<Integer, String> maps = new HashMap(){{
put(2, "abc"); put(3, "def"); put(4, "ghi"); put(5, "jkl"); put(6, "mno"); put(7, "pqrs"); put(8, "tuv"); put(9, "wxyz");
}};
public static List<String> letterCombinations(String digits) {
int size = digits.length();
if(digits == null || digits.length() == 0) return new ArrayList<>();
Set<String> coms = new HashSet();
if(size == 1){
String ele = maps.get(Integer.parseInt(digits));
for (int i = 0; i < ele.length(); i++) {
coms.add(String.valueOf(ele.charAt(i)));
}
}else if(size == 2){
String left = maps.get(Integer.parseInt(String.valueOf(digits.charAt(0))));
String right = maps.get(Integer.parseInt(String.valueOf(digits.charAt(1))));
for (int k = 0; k < left.length(); k++) {
for (int l = 0; l < right.length(); l++) {
coms.add(String.valueOf(left.charAt(k) + String.valueOf(right.charAt(l))));
}
}
}else {
String left = maps.get(Integer.parseInt(String.valueOf(digits.charAt(0))));
List<String> subs = letterCombinations(digits.substring(1, digits.length()));
subs.stream().forEach(item->{
for (int l = 0; l < left.length(); l++) {
coms.add(String.valueOf(left.charAt(l)) + item);
}
});
}
return new ArrayList<>(coms);
}
23. 刪除鏈表的倒數第N個節點
給定一個鏈表,刪除鏈表的倒數第 n 個節點,並且返回鏈表的頭結點。
示例:
給定一個鏈表: 1->2->3->4->5, 和 n = 2. 當刪除了倒數第二個節點后,鏈表變為 1->2->3->5.
說明:
給定的 n 保證是有效的。
進階:
你能嘗試使用一趟掃描實現嗎?
public static ListNode removeNthFromEnd(ListNode head, int n) {
ListNode first = head;
//subIndex 第n個元素的索引
int index = 0, subIndex = 0;
List<ListNode> list = new ArrayList();
while (first != null){
list.add(first);
if(index - subIndex > n){
subIndex++;
}
index++;
first = first.next;
}
if(list.size() < n) return null;
if(list.size() == 1) return null;
if(list.size() == n) return list.get(1);
//處理移除
list.get(subIndex).next = list.get(subIndex).next.next;
return list.get(0);
}
24. 有效的括號
給定一個只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判斷字符串是否有效。
有效字符串需滿足:
- 左括號必須用相同類型的右括號閉合。
- 左括號必須以正確的順序閉合。
注意空字符串可被認為是有效字符串。
//括號映射
public static Map<String, String> maps = new HashMap(){{
put("}", "{");
put("]", "[");
put(")", "(");
put("{", "}");
put("[", "]");
put("(", ")");
}};
public static boolean isValid(String s) {
if(s == null || s.length() == 1) return false;
if(s.length() == 0 ) return true;
int index = 1;
//當前字符
char focus = s.charAt(0);
//存儲非相鄰匹配的符號
List<String> unmap = new LinkedList();
while (index < s.length())
//相鄰對比
if(String.valueOf(s.charAt(index)).equals(maps.get(String.valueOf(focus)))){
//處理存儲的非相鄰匹配的符號匹配
int lIndex = unmap.size() - 1;
if(lIndex > 0) {
unmap.remove(lIndex);
}
while (lIndex > 0){
index++;
lIndex--;
//倒序遍歷匹配
if(unmap.get(lIndex).equals(maps.get(String.valueOf(s.charAt(index))))){
//匹配上則刪除list中元素
unmap.remove(lIndex);
continue;
}else {
//匹配不上則加入到list中
unmap.add(String.valueOf(s.charAt(index)));
index--;
break;
}
}
index++;
if(index >= s.length()) return true;
focus = s.charAt(index);
index++;
}else {
//相鄰不匹配則加入到 list中以供后續使用
if(s.substring(index).contains(String.valueOf(maps.get(String.valueOf(focus)))) && (index + 1 < s.length()) &&s.substring(index + 1).contains(String.valueOf(maps.get(String.valueOf(s.charAt(index)))))){
//第一次非相鄰的時候添加頭一個元素
if(unmap.size() == 0) {
unmap.add(String.valueOf(focus));
}
unmap.add(String.valueOf(s.charAt(index)));
focus = s.charAt(index);
index++;
}else{
return false;
}
}
return false;
}
25. 四數之和
給定一個包含 n 個整數的數組 nums
和一個目標值 target
,判斷 nums
中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target
相等?找出所有滿足條件且不重復的四元組。
注意:
答案中不可以包含重復的四元組。
示例:
給定數組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 滿足要求的四元組集合為: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
/**
* 四數之和
* @param nums
* @param target
* @return
*/
public static List<List<Integer>> fourSum(int[] nums, int target) {
Set<List<Integer>> sets = new HashSet();
List<Integer> numList = new ArrayList();
for (int num : nums) {
numList.add(num);
}
for (int i = 0; i < numList.size(); i++) {
List<Integer> copy = new ArrayList();
copy.addAll(numList);
copy.remove(numList.get(i));
List<List<Integer>> threes = threeSum(copy, target - numList.get(i));
final int finalI = i;
threes.forEach(item -> {
item.add(nums[finalI]);
Collections.sort(item);
sets.add(item);
});
}
return new ArrayList(sets);
}
/**
* 三數之和
* @param nums
* @param target
* @return
*/
public static List<List<Integer>> threeSum(List<Integer> nums, int target) {
if(nums == null || nums.size() < 3) return new ArrayList();
Set<List<Integer>> result = new HashSet<>();
List<Integer> numList = new ArrayList();
for (int num : nums) {
numList.add(num);
}
for (Integer num : numList) {
List<Integer> copy = new ArrayList();
copy.addAll(numList);
copy.remove(num);
List<int[]> tmp = twoSum(copy, target - num);
if(tmp.size()>0){
for (int[] ints : tmp) {
List<Integer> list = new ArrayList(){{add(num);add(ints[0]);add(ints[1]);}};
Collections.sort(list);
result.add(list);
}
}
}
return new ArrayList(result);
}
/**
* 兩數之和
* @param nums
* @param target
* @return
*/
public static List<int[]> twoSum(List<Integer> nums, int target) {
List<int[]> result = new ArrayList();
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums.get(i);
if (map.containsKey(complement)) {
result.add(new int[] { complement, nums.get(i) });
}
map.put(nums.get(i), i);
}
return result;
}
26. 合並兩個有序鏈表
將兩個有序鏈表合並為一個新的有序鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。
示例:
輸入:1->2->4, 1->3->4 輸出:1->1->2->3->4->4
/**
* 合並兩個有序鏈表
* @param l1
* @param l2
* @return
*/
public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null && l2 == null) return null;
List<ListNode> list1 = new ArrayList();
while (l1!= null){
list1.add(l1);
l1 = l1.next;
}
List<ListNode> list2 = new ArrayList();
while (l2!= null){
list2.add(l2);
l2 = l2.next;
}
List<ListNode> result = mergeArrays(list1, list2);
for (int i = 0; i < result.size() - 1; i++) {
result.get(i).next = result.get(i + 1);
}
return result.get(0);
}
public static List<ListNode> mergeArrays(List<ListNode> nums1, List<ListNode> nums2) {
List<ListNode> indexes = new LinkedList();
int lgn1 = nums1.size();
int lgn2 = nums2.size();
int allLgn = lgn1 + lgn2;
int index1 = 0;
int index2 = 0;
for (int i = 0; i < allLgn; i++) {
if(index1 < lgn1 && index2 < lgn2) {
if (nums1.get(index1).val > nums2.get(index2).val) {
indexes.add(nums2.get(index2));
index2++;
} else {
indexes.add(nums1.get(index1));
index1++;
}
}else if(index1 < lgn1){
indexes.add(nums1.get(index1));
index1++;
}else if(index2 < lgn2){
indexes.add(nums2.get(index2));
index2++;
}
}
return indexes;
}
27. 合並K個排序鏈表
合並 k 個排序鏈表,返回合並后的排序鏈表。請分析和描述算法的復雜度。
示例:
輸入: [ 1->4->5, 1->3->4, 2->6 ] 輸出: 1->1->2->3->4->4->5->6
/**
* 合並k個有序鏈表
* @param lists
* @return
*/
public static ListNode mergeKLists(ListNode[] lists) {
if(lists == null || lists.length == 0) return null;
if(lists.length == 1) return lists[0];
int middle = (lists.length + 1)/2;
ListNode left = mergeKLists(Arrays.copyOfRange(lists, 0, middle));
ListNode right = mergeKLists(Arrays.copyOfRange(lists, middle, lists.length));
List<ListNode> list1 = new ArrayList();
while (left!= null){
list1.add(left);
left = left.next;
}
List<ListNode> list2 = new ArrayList();
while (right!= null){
list2.add(right);
right = right.next;
}
List<ListNode> result = mergeArrays(list1, list2);
for (int i = 0; i < result.size() - 1; i++) {
result.get(i).next = result.get(i + 1);
}
return result.size() > 0?result.get(0):null;
}
public static List<ListNode> mergeArrays(List<ListNode> nums1, List<ListNode> nums2) {
List<ListNode> indexes = new LinkedList();
int lgn1 = nums1.size();
int lgn2 = nums2.size();
int allLgn = lgn1 + lgn2;
int index1 = 0;
int index2 = 0;
for (int i = 0; i < allLgn; i++) {
if(index1 < lgn1 && index2 < lgn2) {
if (nums1.get(index1).val > nums2.get(index2).val) {
indexes.add(nums2.get(index2));
index2++;
} else {
indexes.add(nums1.get(index1));
index1++;
}
}else if(index1 < lgn1){
indexes.add(nums1.get(index1));
index1++;
}else if(index2 < lgn2){
indexes.add(nums2.get(index2));
index2++;
}
}
return indexes;
}
28. 兩兩交換鏈表中的節點
給定一個鏈表,兩兩交換其中相鄰的節點,並返回交換后的鏈表。
你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
示例:
給定1->2->3->4
, 你應該返回2->1->4->3
.
public static ListNode swapPairs(ListNode head) {
if(head == null) return null;
ListNode first = head.next == null?head:head.next;
ListNode curr = head;
ListNode pre = null;
while (head != null){
if(head.next != null){
//保存需要交換位置的節點之后的節點
curr = head.next.next;
//將上一個節點和交換后的節點相連
if(pre != null){
pre.next = head.next;
}
//保存下一次需要交換位置節點的上一個節點
pre = head;
//交換節點
head.next.next = head;
//將交換位置后的第二個節點和后面的節點相連
head.next = curr;
}
//繼續遍歷
head = head.next;
}
return first;
}
29. k個一組翻轉鏈表
給出一個鏈表,每 k 個節點一組進行翻轉,並返回翻轉后的鏈表。
k 是一個正整數,它的值小於或等於鏈表的長度。如果節點總數不是 k 的整數倍,那么將最后剩余節點保持原有順序。
示例 :
給定這個鏈表:1->2->3->4->5
當 k = 2 時,應當返回: 2->1->4->3->5
當 k = 3 時,應當返回: 3->2->1->4->5
說明 :
- 你的算法只能使用常數的額外空間。
- 你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
/**
* k個一組翻轉鏈表
* @param head
* @param k
* @return
*/
public static ListNode reverseKGroup(ListNode head, int k) {
if(head == null) return null;
if(k == 1) return head;
if(head.next == null) return head;
List<ListNode> list = new LinkedList();
List<ListNode> listCopy = new LinkedList();
List<ListNode> tmpList = new LinkedList();
int index = 0;
while (head != null){
list.add(head);
head = head.next;
index++;
if(index%k == 0){
tmpList = list.subList(index - k, index);
Collections.reverse(tmpList);
listCopy.addAll(tmpList);
}
}
if(index%k != 0){
tmpList = list.subList(index > k?(index - index%k):0, index);
listCopy.addAll(tmpList);
}
for (int i = 0; i < listCopy.size() - 1; i++) {
listCopy.get(i).next = listCopy.get(i + 1);
}
listCopy.get(listCopy.size() - 1).next = null;
return listCopy.get(0);
}
30. 刪除排序數組中的重復項
給定一個排序數組,你需要在原地刪除重復出現的元素,使得每個元素只出現一次,返回移除后數組的新長度。
不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。
示例 1:
給定數組 nums = [1,1,2], 函數應該返回新的長度 2, 並且原數組 nums 的前兩個元素被修改為1
,2
。 你不需要考慮數組中超出新長度后面的元素。
public static int removeDuplicates(int[] nums) {
if(nums == null || nums.length == 0) return 0;
if(nums.length == 1) return 1;
int index = 0;
int lgn = nums.length;
while (index < lgn){
if(Arrays.binarySearch(nums, index + 1, lgn, nums[index]) > -1){
for (int i = index; i < lgn - 1 ; i++) {
nums[i] = nums[i + 1];
}
nums[lgn - 1] = Integer.MIN_VALUE;
lgn--;
}else {
index++;
}
}
return lgn;
}
31. 移除元素
給定一個數組 nums 和一個值 val,你需要原地移除所有數值等於 val 的元素,返回移除后數組的新長度。
不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。
元素的順序可以改變。你不需要考慮數組中超出新長度后面的元素。
示例 1:
給定 nums = [3,2,2,3], val = 3, 函數應該返回新的長度 2, 並且 nums 中的前兩個元素均為 2。 你不需要考慮數組中超出新長度后面的元素。
public static int removeElement(int[] nums, int val) {
if(nums == null || nums.length == 0) return 0;
int index = 0;
int lgn = nums.length;
while (index < lgn){
if(nums[index] == val){
for (int i = index; i < lgn - 1 ; i++) {
nums[i] = nums[i + 1];
}
nums[lgn - 1] = Integer.MIN_VALUE;
lgn--;
}else {
index++;
}
}
return lgn;
}
32. 實現strStr()
實現 strStr() 函數。
給定一個 haystack 字符串和一個 needle 字符串,在 haystack 字符串中找出 needle 字符串出現的第一個位置 (從0開始)。如果不存在,則返回 -1。
示例 1:
輸入: haystack = "hello", needle = "ll" 輸出: 2
public static int strStr(String haystack, String needle) {
if(haystack == null || needle == null) return -1;
if(haystack.equals(needle) || "".equals(needle)) return 0;
int findex = 0;
int flgn = haystack.length(), slgn = needle.length();
while (findex + slgn <= flgn){
if(haystack.charAt(findex) == needle.charAt(0)) {
int sindex = 0;
for (; sindex < slgn; sindex++) {
if (needle.charAt(sindex) != haystack.charAt(findex + sindex)){
findex = findex + sindex - 1;
break;
}
}
if(sindex == slgn){
return findex;
}else {
findex = findex - sindex + 1;
findex++;
continue;
}
}else {
findex++;
}
}
return -1;
}
待續 。。。 。。。