- 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
思路:设置进位,需要注意的是,如果加到最后一位进位不为0,则需要向前进1
sum = l1+l2+carry mod 10
carry = l1 + l2 + carry / 10
头节点用来返回
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null;
ListNode tail = null;
int carry = 0;
int sum = 0;
while(l1 != null || l2 != null){
int num1 = l1== null? 0:l1.val;
int num2 = l2== null? 0:l2.val;
sum = num1+num2+carry;
if(head == null){
head =tail= new ListNode(sum%10);
}
else{
tail.next = new ListNode(sum%10);
tail = tail.next;
}
carry = sum/10;
if(l1!=null){
l1 = l1.next;
}
if(l2!=null){
l2 = l2.next;
}
}
if(carry > 0){
tail.next = new ListNode(1);
}
return head;
}
}
- 回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
例如,121 是回文,而 123 不是。
示例 1:
输入:x = 121
输出:true
思路:反转整个数字,x%10取出最后一个数,x/10删除最后一个数
class Solution {
public boolean isPalindrome(int x) {
int res = 0;
int backup = x;
if(x < 0){
return false;
}
while(x > 0){
int temp = x % 10;
x = x / 10;
res = res*10 + temp;
}
return backup == res;
}
}
- 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
思路: 双指针,一开始指向一头一尾,计算res,如果左边大,则右值向左移动,反之左值向右
class Solution {
public int maxArea(int[] height) {
int start = 0;
int end = height.length-1;
int res = 0;
while(end > start){
res = Math.max(res,Math.min(height[start],height[end]) * (end-start));
if(height[start] < height[end]){
start++;
}
else{
end--;
}
}
return res;
}
}
- 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
思路:纵向查找,以第一个str作为基准,比较之后的每个对应位置上的char是否一致,如果不一致则直接返回str之前的值
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs.length == 0){
return "";
}
int length = strs[0].length();
int count = strs.length;
for(int i = 0; i < length; i++){
char c = strs[0].charAt(i);
for(int j = 1; j < count; j++){
if(strs[j].length() == i || strs[j].charAt(i) != c){
return strs[0].substring(0,i);
}
}
}
return strs[0];
}
}
- 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
思路:双指针,先对数组进行排序,然后固定一个值,两个指针分别指向固定指针后面数组的头和尾,变成两数之和。需要注意的是,如果发现满足条件的三元组后,不能直接break,应该让左指针右移,右指针左移,接着循环。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
Set<List<Integer>> res = new HashSet<>();
for(int i = 0; i < nums.length; i++){
List<Integer> temp = new ArrayList<>();
int start = i+1;
int end = nums.length-1;
while(start < end){
if(nums[start] + nums[end] == 0 - nums[i]){
temp.add(nums[start]);
temp.add(nums[end]);
temp.add(nums[i]);
res.add(new ArrayList(temp));
start++;
end--;
temp.clear();
}
else if(nums[start] + nums[end] > 0 - nums[i]){
end--;
}
else{
start++;
}
}
}
return new ArrayList(res);
}
}
- 最接近的三数之和
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
思路:类似三数之和,只不过每次保存一个best装三数之和,如果sum和target差值小于best和target的,则更新best
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int best = Integer.MAX_VALUE;
for(int i = 0; i < nums.length; i++){
int start = i+1;
int end = nums.length-1;
while(start < end){
int sum = nums[start] + nums[end] +nums[i];
if(sum == target){
return target;
}
else if(sum > target){
end--;
}
else{
start++;
}
if(Math.abs(sum-target) < Math.abs(best-target)){
best = sum;
}
}
}
return best;
}
}
- 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
思路: 递归回溯法,设置一个visited记录当前节点是否已经被访问过
相当于暴力法,只要当前节点没有访问过就放到output中,访问结束之后删除所有痕迹
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int[] visited = new int[nums.length];
List<Integer> output = new ArrayList<>();
digui(nums,nums.length,0,res,visited,output);
return res;
}
public void digui(int nums[], int n, int first, List<List<Integer>> res,int[] visited, List<Integer> output){
if(first == n){
res.add(new ArrayList(output));
return;
}
for(int i = 0; i < n; i++){
if(visited[i] == 0){
visited[i] = 1;
output.add(nums[i]);
digui(nums,n,first+1,res,visited,output);
output.remove(output.size()-1);
visited[i] = 0;
}
else{
continue;
}
}
}
}
- 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
思路: 模拟
设置一个方向数组,当到边界或者遇到端点的时候就转方向,直到所有格子都走了了一遍,判定端点可以使用辅助数组visited,表示已经走过的点,如果发现这个点已经走过就立即换方向。
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
int row = matrix.length;
int col = matrix[0].length;
int r = 0;
int c = 0;
boolean[][] visited = new boolean[row][col];
int[][] directions = {{0,1},{1,0},{0,-1},{-1,0}};
int directionIndex = 0;
int total = row * col;
for(int i = 0; i < total; i++){
res.add(matrix[r][c]);
visited[r][c] = true;
int nextRow = r + directions[directionIndex][0];
int nextCol = c + directions[directionIndex][1];
if(nextRow == row || nextRow < 0 || nextCol == col || nextCol < 0 || visited[nextRow][nextCol]){
directionIndex = (directionIndex + 1) % 4;
}
r = r + directions[directionIndex][0];
c = c + directions[directionIndex][1];
}
return res;
}
}
- 旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
思路: 模拟
找到分叉点,将tail连接上head,然后在分叉点断开,返回分叉点,要注意的是k可能会大于length,所以需要取模
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head == null){
return null;
}
int length = 0;
ListNode tail = head;
while(tail.next != null){
tail = tail.next;
length++;
}
length++;
int rotate = k % length;
tail.next = head;
for(int i = 0; i < (length-rotate-1); i++){
head = head.next;
}
ListNode res = head.next;
head.next = null;
return res;
}
}
- 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
思路:递归回溯,参照模板写就行
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
digui(nums,0);
return res;
}
public void digui(int[] nums, int position){
res.add(new ArrayList(temp));
if(position >= nums.length){
return;
}
for(int i = position; i < nums.length; i++){
temp.add(nums[i]);
digui(nums,i+1);
temp.remove(temp.size()-1);
}
}
}
- 合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
思路:逆向双指针,从尾开始添加元素,要注意边界的判断,如果一边为-1了就选另一边,直到两个指针都为-1
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m-1;
int j = n-1;
int position = nums1.length-1;
while(i>=0 || j>=0){
if(i < 0){
nums1[position] = nums2[j];
position--;
j--;
continue;
}
else if(j < 0){
nums1[position] = nums1[i];
position--;
i--;
continue;
}
else if( nums1[i] < nums2[j]){
nums1[position] = nums2[j];
position--;
j--;
}
else{
nums1[position] = nums1[i];
position--;
i--;
}
}
}
}
- 格雷编码
n 位格雷码序列 是一个由 2n 个整数组成的序列,其中:
每个整数都在范围 [0, 2n - 1] 内(含 0 和 2n - 1)
第一个整数是 0
一个整数在序列中出现 不超过一次
每对 相邻 整数的二进制表示 恰好一位不同 ,且
第一个 和 最后一个 整数的二进制表示 恰好一位不同
给你一个整数 n ,返回任一有效的 n 位格雷码序列 。
示例 1:
输入:n = 2
输出:[0,1,3,2]
解释:
[0,1,3,2] 的二进制表示是 [00,01,11,10] 。
- 00 和 01 有一位不同
- 01 和 11 有一位不同
- 11 和 10 有一位不同
- 10 和 00 有一位不同
[0,2,3,1] 也是一个有效的格雷码序列,其二进制表示是 [00,10,11,01] 。 - 00 和 10 有一位不同
- 10 和 11 有一位不同
- 11 和 01 有一位不同
- 01 和 00 有一位不同
思路:
class Solution {
public List<Integer> grayCode(int n) {
List<Integer> res = new ArrayList<>();
res.add(0);
int head = 1;
for(int i = 0; i < n; i++){
for(int j = res.size()-1; j >= 0; j--){
res.add(res.get(j)+head);
}
head <<= 1;
}
return res;
}
}
- 买卖股票的最佳时机 II
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
总利润为 4 + 3 = 7 。
思路: dp[i][j] 代表第i天获取的最大利润,j可选0和1,
dp[0][0] 代表第i天不持股,1代表第i天持股
于是,如果当天不持股,则说明可能是前一天持股卖出,也可能是前一天不持股不卖
如果当天持股,则可能是前一天持股今天没卖,或者前一天不持股今天买入
比较最大值,返回最后一天不持股的最大值
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1; i < prices.length; i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[prices.length-1][0];
}
}
- 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
思路: 使用异或,
两个相同的数异或为0,0和任何数异或为原数,
根据异或分配律,所以循环完成后只剩下0和只出现一次的数进行异或,为原数
- 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
思路:存储当前节点cur,前序节点pre,后续节点next,每次先保存next,接着把cur.next设置成pre,pre迁移到cur,cur迁移到next
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null){
return null;
}
ListNode p1 = null;
ListNode p2 = head;
while(p2 != null){
ListNode temp = p2.next;
p2.next = p1;
p1 = p2;
p2 = temp;
}
return p1;
}
}
- 数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
思路 1: 基于快排
首先使用partition找到末尾数字改在的位置,并且返回该位置,之后判断此位置是否为k,如果比k小,移动right到index-1,反之亦然
class Solution {
public int findKthLargest(int[] nums, int k) {
int left = 0;
int right = nums.length-1;
while(true){
int index = partition(nums,left,right);
if(index == nums.length-k){
return nums[index];
}
else if(index > nums.length-k){
right = index-1;
}
else{
left = index+1;
}
}
}
public void quickSort(int[] arr, int begin, int end) {
if (begin < end){
int p = partition(arr, begin, end);
// int p = partition2(arr, begin, end);
quickSort(arr, begin, p - 1);
quickSort(arr, p + 1, end);
}
}
public int partition(int[] arr, int start, int end) {
int point = arr[end];
int i = start;
int j = end;
while (i < j) {
while(i < j && arr[i] <= point) ++i;//注意这里时小于等于
while(i < j && arr[j] >= point) --j;
if(i < j){
swap(arr,i,j);
}
}
swap(arr,i,end);
return i;
}
public void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
思路二大顶堆:
class Solution {
public int findKthLargest(int[] nums, int k) {
buildHeap(nums, nums.length);
int heapSize = nums.length;
for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
swap(nums, 0, i);
--heapSize;
maxHeap(nums, 0, heapSize);
}
return nums[0];
}
public void buildHeap(int[] nums,int size){
for(int i = size/2; i >= 0; i--){
maxHeap(nums,i,size);
}
}
public void maxHeap(int[] nums, int i,int size){
int largest = i;
int l = 2*i + 1;
int r = 2*i + 2;
if(l < size && nums[l] > nums[largest]){
largest = l;
}
if(r < size && nums[r] > nums[largest]){
largest = r;
}
if(largest != i){
swap(nums,i,largest);
maxHeap(nums,largest,size);
}
}
public void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}