203. 移除链表元素
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode node = new ListNode(-1);
node.next = head;
ListNode cur = node ;
ListNode next = cur.next;
while(next != null){
if(next.val == val){
cur.next = next.next;
}else{
cur = next;
}
next = next.next;
}
return node.next;
}
}
206. 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode node = new ListNode(-1);
ListNode cur = head;
while(cur != null){
ListNode temp = cur.next;
cur.next = node.next;
node.next = cur;
cur = temp;
}
return node.next;
}
}
283. 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/move-zeroes
class Solution {
public void moveZeroes(int[] nums) {
int index = 0 ;
for (int i = 0; i < nums.length; i++) {
if(nums[i] != 0){
nums[index ++] = nums[i];
}
}
for (int i = index; i < nums.length; i++) {
nums[i] = 0;
}
}
}
771. 宝石与石头
给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。
J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a"和"A"是不同类型的石头。
示例 1:
输入: J = "aA", S = "aAAbbbb"
输出: 3
示例 2:
输入: J = "z", S = "ZZ"
输出: 0
注意:
S 和 J 最多含有50个字母。
J 中的字符不重复。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jewels-and-stones
class Solution {
public int numJewelsInStones(String J, String S) {
HashMap map = new HashMap<>();
for (int i = 0; i < J.length(); i++) {
map.put(J.charAt(i), 0);
}
int result= 0;
for (int i = 0; i < S.length(); i++) {
if(map.containsKey(S.charAt(i))){
result++;
}
}
return result;
}
}
234. 回文链表
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
思路:
只要找到链表的中间位置,以中间位置为分界线,反转前半部分,再用反转了的前半部分与后半部分做对比,如有不同就返回false
这一种做法虽然有两次遍历,但两次遍历的长度均为链表个数的一半,所以达到时间复杂度为O(n)
做法:
获取中间值:
设置一个中间指针mid,在一次遍历中,head走两格,mid走一格,当head取到最后一个值或者跳出时,mid就指向中间的值
ListNode mid = head;
// 循环条件:只要head存在则最少走一次
while(head !== null && head.next !== null) {
head = head.next.next // 指针一次走两格
mid = mid.next// 中间指针一次走一格
}
反转前部分节点:
遍历的时候通过迭代来反转链表,mid之前的node都会被反转
使用迭代来反转
while(head != null && head.next != null ){
// 这个赋值要在mid被修改前提前
pre = mid;
// 遍历链表
mid = mid.next;
head = head.next.next;
// 反转前面部分的节点,并用reversed保存
pre.next = reversed.next;
reversed.next = pre;
}
注意:
奇数偶数的情况略有不同,奇数情况下,在判断值是否相同时mid要往后走一位
例如:
奇数:1 -> 2 -> 3 -> 2 ->1
遍历完成后:mid = 3->2->1
reversed = 2->1
偶数:1 -> 2 -> 2 ->1
遍历完成后:mid = 2->1
reversed = 2->1
完整代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null)
return true;
ListNode mid = head;
ListNode pre = null;
ListNode reversed = new ListNode(-1);
// // end每次走两格,这个循环的时间复杂度为O(n/2)
while(head != null && head.next != null ){
// 这个赋值要在mid被修改前提前
pre = mid;
// 遍历链表
mid = mid.next;
head = head.next.next;
// 反转前面部分的节点,并用reversed保存
pre.next = reversed.next;
reversed.next = pre;
}
// 奇数mid往后走一位
if(head != null){
mid = mid.next;
}
reversed = reversed.next;
while(mid != null){
if(reversed.val != mid.val) return false;
reversed= reversed.next;
mid = mid.next;
}
return true;
}
}
198. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber
动态规划
考虑所有可能的抢劫方案过于困难。一个自然而然的想法是首先从最简单的情况开始。记:
f(k) = 从前 k 个房屋中能抢劫到的最大数额,A i= 第 i 个房屋的钱数。
首先看 n = 1 的情况,显然 f(1) = A 1。
再看 n = 2,f(2) = max(A1, A2)。
对于 n = 3,有两个选项:
抢第三个房子,将数额与第一个房子相加。
不抢第三个房子,保持现有最大数额。
显然,你想选择数额更大的选项。于是,可以总结出公式:
f(k) = max(f(k – 2) + Ak, f(k – 1))
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if(len == 0)
return 0;
int[] dp = new int[len + 1];
dp[0] = 0;
dp[1] = nums[0];
for(int i = 2; i <= len; i++) {
dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]);
}
return dp[len];
}
}
125. 验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:
输入: "race a car"
输出: false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-palindrome
class Solution {
public boolean isPalindrome(String s) {
StringBuilder sb = new StringBuilder();
s = s.toLowerCase();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c >= 'a' && c <= 'z' || c >= '0' && c<= '9'){
sb.append(c);
}
}
return sb.toString().equals(sb.reverse().toString());
}
}
415. 字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。
注意:
num1 和num2 的长度都小于 5100.
num1 和num2 都只包含数字 0-9.
num1 和num2 都不包含任何前导零。
你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-strings
class Solution {
public String addStrings(String num1, String num2) {
int len1 = num1.length() -1;
int len2 = num2.length() -1 ;
int ans = 0;
StringBuilder sb = new StringBuilder();
while(len1 >=0 || len2 >= 0){
char c1 = len1 >=0 ? num1.charAt(len1):'0';
char c2 = len2 >=0 ?num2.charAt(len2) : '0';
sb.append((c1 +c2 - 48 - 48 + ans )%10);
ans = (c1 +c2 - 48 - 48 + ans)/10;
len1--;
len2--;
}
if(ans == 1){
sb.append('1');
}
return sb.reverse().toString();
}
}
43. 字符串相乘
给定两个以字符串形式表示的非负整数 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)或直接将输入转换为整数来处理。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/multiply-strings
竖式乘法
class Solution {
public String multiply(String num1, String num2) {
char[] ret = new char[num1.length() + num2.length()];
Arrays.fill(ret, '0');
for(int i = num1.length() - 1; i >= 0; i --) {
int num1Val = num1.charAt(i) - '0';
for(int j = num2.length() - 1; j >= 0; j --) {
int num2Val = num2.charAt(j) - '0';
int sum = (ret[i + j + 1] - '0') + num1Val * num2Val;
ret[i + j + 1] = (char)(sum % 10 + '0');//当前位
ret[i + j] += sum / 10;
//前一位加上进位,res[i+j]已经初始化为'0',加上int类型自动转化为char,所以此处不加'0'
}
}
//去除首位'0'
for(int i = 0; i < ret.length; i ++) {
if(ret[i] != '0' ) {
return new String(ret, i, ret.length - i);
}
}
return "0";
}
}
24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode node = new ListNode(-1);
node.next = head;
ListNode cur = node;
ListNode i = null;
ListNode j = null;
while(cur.next != null && cur.next.next != null){
i = cur.next;
j = i .next;
i.next = j.next;
j.next = cur.next;
cur.next = j ;
cur = i ;
}
return node.next;
}
}
557. 反转字符串中的单词 III
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例 1:
输入: "Let's take LeetCode contest"
输出: "s'teL ekat edoCteeL tsetnoc"
注意:在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-words-in-a-string-iii
class Solution {
public String reverseWords(String s) {
StringBuilder sb = new StringBuilder( );
String[] split = s.split(" ");
for (int i = 0; i < split.length; i++) {
sb.append(new StringBuilder(split[i]).reverse()+" ");
}
return sb.toString().trim();
}
}
1108. IP 地址无效化
给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。
所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 "."。
示例 1:
输入:address = "1.1.1.1"
输出:"1[.]1[.]1[.]1"
示例 2:
输入:address = "255.100.50.0"
输出:"255[.]100[.]50[.]0"
提示:
给出的 address 是一个有效的 IPv4 地址
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/defanging-an-ip-address
class Solution {
public String defangIPaddr(String address) {
String[] split = address.split("\\.");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < split.length -1; i++) {
sb.append(split[i]);
sb.append("[.]");
}
sb.append(split[split.length - 1]);
return sb.toString();
}
}
461. 汉明距离
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x 和 y,计算它们之间的汉明距离。
注意:
0 ≤ x, y < 2^31.
示例:
输入: x = 1, y = 4
输出: 2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭头指出了对应二进制位不同的位置。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/hamming-distance
class Solution {
public int hammingDistance(int x, int y) {
int temp = x ^ y;
int result = 0;
while(temp != 0 ){
result += temp & 1;
temp = temp >> 1;
}
return result;
}
}
189. 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-array
public void rotate(int[] nums, int k) {
k = k % nums.length;
swap(nums, 0, nums.length -1);
swap(nums, 0, k-1);
swap(nums, k, nums.length -1);
}
public void swap(int[] nums ,int start ,int end){
for (int i = start ,j = end ; i < j; i++,j--) {
int temp = nums[i];
nums[i] = nums[j] ;
nums[j] = temp;
}
}
832. 翻转图像
给定一个二进制矩阵 A,我们想先水平翻转图像,然后反转图像并返回结果。
水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。
反转图片的意思是图片中的 0 全部被 1 替换, 1 全部被 0 替换。例如,反转 [0, 1, 1] 的结果是 [1, 0, 0]。
示例 1:
输入: [[1,1,0],[1,0,1],[0,0,0]]
输出: [[1,0,0],[0,1,0],[1,1,1]]
解释: 首先翻转每一行: [[0,1,1],[1,0,1],[0,0,0]];
然后反转图片: [[1,0,0],[0,1,0],[1,1,1]]
示例 2:
输入: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]
输出: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
解释: 首先翻转每一行: [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]];
然后反转图片: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
说明:
1 <= A.length = A[0].length <= 20
0 <= A[i][j] <= 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/flipping-an-image
/*
* 我们可以不使用额外的(非常数)空间来完成翻转和反转操作。对于 A[i][j],
* 我们将它和 A[i][c - j - 1] 进行交换(即翻转),其中 c 是数组 A 的列数。
* 在交换的同时,我们可以将这两个数进行反转。
*/
public int[][] flipAndInvertImage(int[][] A) {
int c = A[0].length;
for (int[] row: A)
for (int i = 0; i < (c + 1) / 2; ++i) {
int tmp = row[i] ^ 1;
row[i] = row[c - 1 - i] ^ 1;
row[c - 1 - i] = tmp;
}
return A;
}
509. 斐波那契数
斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
给定 N,计算 F(N)。
示例 1:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.
示例 2:
输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2.
示例 3:
输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3.
提示:
0 ≤ N ≤ 30
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fibonacci-number
class Solution {
public int fib(int N) {
int a = 0 ;
int b = 1;
if(N == 1){
return N;
}
int tar = 0;
for (int i = 2; i <= N; i++) {
tar = a + b;
a = b;
b = tar;
}
return tar;
}
}
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-anagram
方法一:排序
算法:
通过将 s 的字母重新排列成 t来生成变位词。因此,如果 T 是 S 的变位词,对两个字符串进行排序将产生两个相同的字符串。此外,如果 s 和 t 的长度不同,t不能是 s 的变位词,我们可以提前返回。
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()){
return false;
}
char[] charArray = s.toCharArray();
char[] charArray2 = t.toCharArray();
Arrays.sort(charArray);
Arrays.sort(charArray2);
return Arrays.equals(charArray, charArray2);
}
方法二:哈希表
算法:
为了检查 t是否是 s 的重新排列,我们可以计算两个字符串中每个字母的出现次数并进行比较。因为 S 和 T 都只包含 A−Z 的字母,所以一个简单的 26 位计数器表就足够了。
我们需要两个计数器数表进行比较吗?实际上不是,因为我们可以用一个计数器表计算 s字母的频率,用 t 减少计数器表中的每个字母的计数器,然后检查计数器是否回到零。
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] counter = new int[26];
for (int i = 0; i < s.length(); i++) {
counter[s.charAt(i) - 'a']++;
counter[t.charAt(i) - 'a']--;
}
for (int count : counter) {
if (count != 0) {
return false;
}
}
return true;
}
或者我们可以先用计数器表计算 s,然后用 t 减少计数器表中的每个字母的计数器。如果在任何时候计数器低于零,我们知道 t 包含一个不在 s 中的额外字母,并立即返回 FALSE。
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] table = new int[26];
for (int i = 0; i < s.length(); i++) {
table[s.charAt(i) - 'a']++;
}
for (int i = 0; i < t.length(); i++) {
table[t.charAt(i) - 'a']--;
if (table[t.charAt(i) - 'a'] < 0) {
return false;
}
}
return true;
}
217. 存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/contains-duplicate
class Solution {
public boolean containsDuplicate(int[] nums) {
HashSet set = new HashSet<>(nums.length);
for (int i = 0; i < nums.length; i++) {
if(set.contains(nums[i])){
return true;
}else{
set.add(nums[i]);
}
}
return false;
}
}
387. 字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
案例:
s = "leetcode"
返回 0.
s = "loveleetcode",
返回 2.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-unique-character-in-a-string
class Solution {
public int firstUniqChar(String s) {
HashMap count = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
count.put(c, count.getOrDefault(c, 0)+1);
}
for (int i = 0; i < s.length(); i++) {
if(count.get(s.charAt(i)) == 1 )
return i;
}
return -1;
}
}