leetcode刷题笔记(1)——每日10题2021.12.29
不要觉得每道题都有很巧妙的方法可以解出来,只是自己没想到;有思路就试着将其实现,尽可能写出来再看其他解法。
链表
(1)23.
(2)最长回文子串
我的思路:用一个方法判断字符串是否为回文字符串;主方法中,从长度为字符串的长度出发,判断是否为回文字符串,不是则长度减一,重新搜索。
class Solution {
public String longestPalindrome(String s) {
if(s.length() <= 1){
return s;
}
for(int i = s.length(); i > 0; i--){
for(int j = 0; j + i - 1 < s.length(); j++){
if(palindrome(s.substring(j,j+i))){
return s.substring(j,j+i);
}
}
}
return s.substring(1);
}
public static boolean palindrome(String t){
if(t.length() <= 1){
return true;
}
int first = 0;
int last = t.length() - 1;
while(t.charAt(first) == t.charAt(last)){
first++;
last--;
if(first >= last){
return true;
}
}
return false;
}
}
法二:中心扩散法
class Solution {
public String longestPalindrome(String s) {
if(s.length() <= 1){
return s;
}
int start = 0;
int end = 0;
int maxLen = 1;
for(int i = 0;i < s.length()-1; i++){
int len1 = entrySpread(s,i,i);
int len2 = entrySpread(s,i,i+1);
int len = len1 > len2 ? len1:len2;
if(len > maxLen){
start = i - (len-1)/2;
end = i + len / 2;
maxLen = len;
}
}
return s.substring(start,end+1);
}
public static int entrySpread(String s,int left,int right){
while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){
left--;
right++;
}
return right - left - 1; //注意边界
}
}
法三:动态规划
class Solution {
public String longestPalindrome(String s) {
if(s.length() <= 1){
return s;
}
boolean[][] dp = new boolean[s.length()][s.length()];
for(int i = 0; i < s.length(); i++){
dp[i][i] = true;
}
char[] charArray = s.toCharArray();
for(int L = 2; L <= s.length(); L++){
for(int i = 0; i + L <= s.length(); i++){
int j = i + L - 1;
if(charArray[i] != charArray[j]){
dp[i][j] = false;
}else{
if(j - i < 3){
dp[i][j] = true;
}else{
dp[i][j] = dp[i+1][j-1];
}
}
}
}
int maxLen = 1;
int start = 0;
int end = 0;
for(int i = 0; i < s.length();i++){
for(int j = i; j < s.length();j++){
if(dp[i][j] && j-i+1 > maxLen){
maxLen = j-i+1;
start = i;
end = j;
}
}
}
return s.substring(start,end+1);
}
}
(3)25. K个一组翻转链表(错1)
递归
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if(head == null || k <= 1){
return head;
}
ListNode pre = null;
ListNode cur = head;
ListNode temp = null;
ListNode first = cur;
int canProceed = 0;
int count = 0;
while(canProceed < k && first != null){
first = first.next;
canProceed++;
}
if(canProceed == k){
while(count < k && cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
count++;
}
if(temp != null){
head.next = reverseKGroup(temp,k);
}
return pre;
}else{
return head;
}
}
}
(4)旋转链表
我的思路:遍历一边链表,记录链表长度,找到切割点,然后再遍历一遍,进行切割
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head == null || k == 0){
return head;
}
int count = 0;
ListNode cur = head;
while(cur != null){
count++;
cur = cur.next;
}
k = k % count;
int t = count - k;
cur = head;
for(int i = 0; i < t-1;i++){
cur = cur.next;
}
ListNode temp = cur.next;
cur.next = null;
cur = temp;
if(temp == null){
return head;
}
while(temp.next != null){
temp = temp.next;
}
temp.next = head;
return cur;
}
}
(5)83. 删除排序链表中的重复元素
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode pre = head;
ListNode cur = head.next;
while(cur != null){
if(pre.val == cur.val){
pre.next = cur.next;
cur = pre.next;
}else{
cur = cur.next;
pre = pre.next;
}
}
return head;
}
}
(6)82. 删除排序链表中的重复元素 II
我的思路:创建一个不同值的头节点,如遇到重复值的节点,记录重复节点的值,往后找到与该值不同的节点为止,将pre.next指向这个节点。
小结:如果遇到不好处理的头节点,可以新建一个头节点。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null){
return head;
}
head = new ListNode(head.val-1,head);
ListNode pre = head;
ListNode cur = head.next;
while(cur != null && cur.next != null){
if(cur.val == cur.next.val){
int temp = cur.val;
while(cur != null && cur.val == temp){
cur = cur.next;
}
pre.next = cur;
}else{
pre = pre.next;
cur = cur.next;
}
}
return head.next;
}
}
(7)86. 分隔链表
我的思路:创建一个新链表,将值小于x的节点复制到新链表中,原来的链表删除掉这样的节点;然后将两条链表拼接。
class Solution {
public ListNode partition(ListNode head, int x) {
if(head == null || head.next == null){
return head;
}
ListNode newHead = new ListNode(x-1);
ListNode newCur = newHead;
head = new ListNode(x-1,head);
ListNode pre = head;
ListNode cur = head.next;
while(cur != null){
if(cur.val < x){
newCur.next = new ListNode(cur.val);
newCur = newCur.next;
pre.next = cur.next;
cur = pre.next;
}else{
pre = pre.next;
cur = cur.next;
}
}
newCur.next = head.next;
return newHead.next;
}
}
题解思路:创建两个链表分别记录小于x的节点,和其他节点,没有复制节点,然后将两个链表合并
class Solution {
public ListNode partition(ListNode head, int x) {
if(head == null || head.next == null){
return head;
}
ListNode small = new ListNode(x-1);
ListNode smallHead = small;
ListNode large = new ListNode(x-1);
ListNode largeHead = large;
while(head != null){
if(head.val < x){
small.next = head;
small = small.next;
}else{
large.next = head;
large = large.next;
}
head = head.next;
}
large.next = null; //避免成环
small.next = largeHead.next;
return smallHead.next;
}
}
(8)92. 反转链表 II
我的思路:用新节点作为头节点,找到反转链表开始的位置,用一个空节点作为反转部分的头,反转len次之后,反转部分与后面的链表是分离的,将三部分链表拼接起来。
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head == null || head.next == null || left == right){
return head;
}
int len = right - left + 1;
ListNode pre = new ListNode(0);
ListNode newHead = pre;
pre.next = head;
ListNode cur = head;
ListNode prepre = null;
ListNode temp = null;
for(int i = 0; i < left-1; i++){
pre = pre.next;
cur = cur.next;
}
while(len > 0){
temp = cur.next;
cur.next = prepre;
prepre = cur;
cur = temp;
len--;
}
pre.next = prepre;
while(prepre.next != null){
prepre = prepre.next;
}
prepre.next = cur;
return newHead.next;
}
}
题解思路:一次遍历完成,穿针引线。
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head == null || head.next == null || left == right){
return head;
}
int len = right - left + 1;
ListNode newHead = new ListNode(0);
newHead.next = head;
ListNode pre = newHead;
ListNode cur = null;
ListNode temp = null;
for(int i = 0; i < left-1; i++){
pre = pre.next;
}
cur = pre.next;
for(int i = 0 ;i < right - left;i++){
temp = cur.next;
cur.next = temp.next;
temp.next = pre.next;
pre.next = temp;
}
return newHead.next;
}
}
(9)109. 有序链表转换二叉搜索树(错1)
题解思路:快慢指针找到中间节点,作为根节点,递归构造平衡二叉树。
class Solution {
public TreeNode sortedListToBST(ListNode head) {
if(head == null){
return null;
}
if(head.next == null){
return new TreeNode(head.val);
}
ListNode pre = head;
ListNode slow = pre.next;
ListNode fast = slow.next;
while(fast != null && fast.next != null){
pre = pre.next;
slow = pre.next;
fast = fast.next.next;
}
TreeNode root = new TreeNode(slow.val);
pre.next = null;
slow = slow.next;
root.left = sortedListToBST(head);
root.right = sortedListToBST(slow);
return root;
}
}
(10)39. 组合总和(错1)
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
int len = candidates.length;
if(len == 0){
return res;
}
Arrays.sort(candidates);
Deque<Integer> path = new ArrayDeque<>();
drawBack(candidates, 0, len, target, path, res);
return res;
}
public static void drawBack(int[] candidates, int begin, int len, int target,Deque<Integer> path,List<List<Integer>> res){
if(target == 0){
res.add(new ArrayList<>(path));
return;
}
for(int i = begin;i < len; i++){
if(target-candidates[i] < 0){
break;
}
path.addLast(candidates[i]);
drawBack(candidates, i, len, target-candidates[i], path, res);
path.removeLast();
}
}
}