文章目录
记录leetcode刷题的笔记,参考了leetcode的高赞答案和官方解答.
二进制求和leetcode67
给定两个二进制字符串,返回他们的和(用二进制表示)。
输入为非空字符串且只包含数字 1 和 0。
示例 1:
输入: a = “11”, b = “1”
输出: “100”
示例 2:
输入: a = “1010”, b = “1011”
输出: “10101”
思路:
- 将两个字符串进行比较,将两个字符串扩展为统一长度.这个过程不能使用string.format函数,否则在转化为Integer类型时会发生越界错误.所以可以利用建立一个新的字符串来解决
- 从字符串的尾部开始遍历,建立两个变量,t,c.c表示上一位是否有进位,如果没有则是0,否则为1.t用来记录同位置的两数相加(如果c为1也要加上去)
- StringBuffer最后append的结果为(char)(t%2+‘0’).
public String addBinary(String str1, String str2) {
int length = Math.max(str1.length(), str2.length());
String extendedStr1 = "";
//将两个字符串转化为统一长度
if (str1.length() == str2.length()) {
} else if (str1.length() < str2.length()) {
for (int i = str1.length(); i < length; i++) {
extendedStr1 += "0";
}
extendedStr1 += str1;
str1 = extendedStr1;
} else {
for (int i = str2.length(); i < length; i++) {
extendedStr1 += "0";
}
extendedStr1 += str2;
str2 = extendedStr1;
}
StringBuffer ans = new StringBuffer();
int t = 0, c = 0;
//进行二进制运算
for (int i = length - 1; i >= 0; i--) {
t += str1.charAt(i) - '0';
t += str2.charAt(i) - '0';
if (c == 0) {
if (t > 1) {
c = 1;
ans.append((char) (t % 2 + '0'));
} else {
ans.append((char) (t % 2 + '0'));
}
} else {
t += c;
if (t > 1) {
ans.append((char) (t % 2 + '0'));
} else {
c = 0;
ans.append((char) (t % 2 + '0'));
}
}
t = 0;
}
if (c == 1) {
ans.append('1');
}
//返回结果
return ans.reverse().toString();
}
移除元素leetcode27
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
思路:双指针法,
- 建立两个指针,left,right.
- 遍历数组,如果nums[right]!=val,则将nums[left]=nums[right],left++,right++.
- 最后返回left.
public int removeElement(int[] nums, int val) {
int n = nums.length;
int left = 0;
for (int right = 0; right < n; right++) {
if (nums[right] != val) {
nums[left] = nums[right];
left++;
}
}
return left;
}
删除排序数组中的重复项leetcode26
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度
。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
思路:双指针法,
最后返回left+1.
public int removeDuplicates(int[] nums) {
int n = nums.length;
int left = 0;
for (int right = 1; right < n; right++) {
if (nums[left] != nums[right]) {
left++;
nums[left] = nums[right];
}
}
return left + 1;
}
删除排序数组中的重复项IIleetcode80
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素最多出现两次
,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1)
额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 1, 1, 2, 2, 3
。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,1,2,3,3],
函数应该返回新的长度 7, 并且原数组 nums 的前七个元素被修改为 0, 0, 1, 1, 2, 3, 3
。
你不需要考虑数组中超出新长度后面的元素。
思路:双指针法
88. 合并两个有序数组
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
思路:双指针法,需要注意的是,我们无序创建新的数组:由于nums1的长度为m+n,nums2的长度为n,所以我们可以直接在nums1上进行操作,将nums2合并到nums1上.可以指针设置为从后向前遍历,每次取两者之中的较大者放进 nums1的最后面。
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i=m+n-1,a=m-1,b=n-1;
while(a>-1||b>-1){
if(a==-1){
nums1[i]=nums2[b];
b--;i--;
}else if(b==-1){
nums1[i]=nums1[a];
a--;i--;
}else if(nums1[a]>=nums2[b]){
nums1[i]=nums1[a];
a--;i--;
}else{
nums1[i]=nums2[b];
b--;i--;
}
}
}
4. 寻找两个正序数组的中位数
给定两个大小为 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
思路:切割法
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) {
return findMedianSortedArrays(B, A); // 保证 m <= n
}
int min = 0;
int max = m;
while (min <= max) {
int i = (min + max) / 2;
int j = (m + n + 1) / 2 - i;
if (i != 0 && j != n && A[i - 1] > B[j]) {//i需要减小
max = i - 1;
} else if (j != 0 && i != m && B[j - 1] > A[i]) {//i需要增大
min = i + 1;
} else {
int ml = 0;
if (i == 0) {
ml = B[j - 1];
} else if (j == 0) {
ml = A[i - 1];
} else {
ml = Math.max(A[i - 1], B[j - 1]);
}
if ((m + n) % 2 == 1) return ml;//如果是奇数,就不用考虑切割的右边
int mr = 0;
if (j == n) {
mr = A[i];
} else if (i == m) {
mr = B[j];
} else {
mr = Math.min(A[i], B[j]);
}
return (ml + mr) / 2.0;//如果是偶数就返回结果
}
}
return 0.0;
}
382.链表随机节点
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
思路:使用一个数组来存储链表的节点值,然后使用随机数来随机选择数组中的一个元素.
复杂度分析:
- 时间复杂度:初始化为 O ( n ) O(n) O(n),随机选择为 O ( 1 ) O(1) O(1)
- 空间复杂度: O ( n ) O(n) O(n)
List<Integer> list = new ArrayList<Integer>();
Random random = new Random();
public Solution(ListNode head) {
while (head != null) {
list.add(head.val);
head = head.next;
}
}
public int getRandom() {
return list.get(random.nextInt(list.size()));
}
思路:水塘抽样
从链表头开始,遍历整个链表,对遍历到的第 i 个节点,随机选择区间 [0,i) 内的一个整数,如果其等于 0,则将答案置为该节点值,否则答案不变。
该算法会保证每个节点的成为最后被返回的值的概率均为
1
n
\frac{1}{n}
n1,证明如下:
P
(
第
i
个节点的值成为最后被返回的值
)
=
P
(
第
i
随机选择的值为0
)
×
P
(
第
i
+
1
次随机选择的值不为0
×
⋯
×
P
(
第
n
次随机选择的值不为0
)
)
=
1
i
×
i
−
1
i
+
1
×
⋯
×
n
−
i
+
1
n
=
1
n
\begin{aligned} &P(\text{第}i\text{个节点的值成为最后被返回的值})\\&= P(\text{第}i\text{随机选择的值为0})\times P(\text{第}i+1\text{次随机选择的值不为0}\times\cdots\times P(\text{第}n\text{次随机选择的值不为0}))\\&= \frac{1}{i}\times\frac{i-1}{i+1}\times\cdots\times\frac{n-i+1}{n}\\&= \frac{1}{n} \end{aligned}
P(第i个节点的值成为最后被返回的值)=P(第i随机选择的值为0)×P(第i+1次随机选择的值不为0×⋯×P(第n次随机选择的值不为0))=i1×i+1i−1×⋯×nn−i+1=n1
class Solution {
ListNode head;
Random random;
public Solution(ListNode head) {
this.head = head;
random = new Random();
}
public int getRandom() {
int i = 1, ans = 0;
for (ListNode node = head; node != null; node = node.next) {
if (random.nextInt(i) == 0) { // 1/i 的概率选中(替换为答案)
ans = node.val;
}
++i;
}
return ans;
}
}
203.移除链表元素
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
思路:由于head节点本身可能会被删除,所以要指定一个node节点,它的next节点还head,再通过temp节点来遍历整个链表.删除相应的节点即可.
public ListNode removeElements(ListNode head, int val) {
ListNode node = new ListNode(0);
node.next=head;
ListNode temp = node;
while(temp.next!=null){
if(temp.next.val==val){//删除
temp.next=temp.next.next;
}else{
temp = temp.next;
}
}
return node.next;
}
237.删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
思路:由于要删除的是node节点,所以考虑将node.next复制到node上,然后删除node.next即可.
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
328.奇偶链表
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
- 思路:使用两个链表分别存储奇数和偶数节点,然后将奇数链表的最后一个节点指向偶数链表的头节点即可.
public ListNode oddEvenList(ListNode head) {
if (head == null) return null;
ListNode odd = head;//奇数链表
ListNode even = odd.next;//偶数链表
ListNode temp = even;//偶数头节点
while (odd.next != null && odd.next.next != null) {
odd.next = odd.next.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = temp;
return head;
}
86.分隔链表
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
public ListNode partition(ListNode head, int x) {
ListNode dummy = new ListNode(-1);dummy.next=head;
ListNode p = head;//head记录的是小于x最右端的节点位置
//pre记录p之前的节点,p遍历所有节点
ListNode pre = dummy;//刚开始时,dummy与pre是同一个,p与head是同一个
head = dummy;
while (p!=null) {
if (p.val<x) {
pre.next = p.next;//这一步和下一步不能调换,否则由于p和head一开始相同,会出现循环指的问题
p.next = head.next;
head.next = p;//第一次循环时,这样得到的结果就是:链表结构不改变
// 往后每次循环时,这样得到的结果就是:p指的节点(小于x的节点)会被插入到head的next位置
// 并且不会改变原有的结构.
head = p;
pre = p;
p = pre.next;//循环一次之后,pre和head是同一个,p在head和pre的next位置
//循环之后,pre和head变成了p所在的位置,而p处于pre.next位置,继续下一次的循环
} else {
p = p.next;
pre = pre.next;
}
}
return dummy.next;
}
24.两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
输入:head = [1,2,3,4]
输出:[2,1,4,3]
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp=dummyHead;
while(temp.next!=null&&temp.next.next!=null){
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;
temp.next = node2;
node1.next=node2.next;
node2.next = node1;
temp=node1;
}
return dummyHead.next;
}
876.链表的中间结点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
- 思路:快慢指针法(注意跳出循环的条件)
public ListNode middleNode(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode p = head.next;
ListNode q = head.next.next;
while(q!=null&&q.next!=null){
p=p.next;
q=q.next.next;
}
return p;
}
234.回文链表
请判断一个链表是否为回文链表。
思路:看注释,注意while的条件,注意prepre的初始值为null,注意判断奇偶性的fast可能取值,
public boolean isPalindrome(ListNode head) {
//如果头为空或者链表就一个数,则为true
if (head == null || head.next == null) {
return true;
}
//slow慢指针,fast快指针,fast遍历到尾巴时,slow刚好到中间
//pre和prepre作为反转前半链表的操作指针
//pre紧贴slow,而prepre作为pre的next,即原链表中pre的前一个节点
ListNode slow = head, fast = head;
ListNode pre = head, prepre = null;
//当fast节点遍历到尾巴或者遍历到尾巴的下一个(null)跳出
while (fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
//开始反转前半链表
//先让在前的pre改变原指向后的链条,而去指向prepre,即pre的前一个节点,即反转
//再让prepre在新的“前端”等待下一次反转
pre.next = prepre;
prepre = pre;
}
//当fast!=null,表示链表为奇数个,此时slow在中间,需要把slow往后移动一个到后半对称的开始
if (fast != null) {
slow = slow.next;
}
//此时,slow为后半链条的头,pre为前半链条的头,开始循环比较即可
while (slow != null && pre != null) {
if (slow.val != pre.val) {
return false;
}
slow = slow.next;
pre = pre.next;
}
return true;
}
143.重排链表
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
- 注意到目标链表为原链表的左半端和反转后的右半端合并后的结果,所以任务分为三步
- 1.找到链表的中点,利用快慢指针实现
- 2.将链表的后半段反转
- 3.将前后两部分合并
public void reorderList(ListNode head) {
if (head == null || head.next == null) {
return;
}
ListNode middle = middle(head);
ListNode l1 = head;
ListNode l2 = middle.next;
middle.next = null;
l2 = reverse(l2);
merge(l1, l2);
}
public ListNode middle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
public ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode nextTemp = cur.next;
cur.next = pre;
pre = cur;
cur = nextTemp;
}
return pre;
}
public void merge(ListNode l1, ListNode l2) {
ListNode l1_tem;
ListNode l2_tem;
while (l1 != null && l2 != null) {
l1_tem = l1.next;
l2_tem = l2.next;
l1.next = l2;
l1 = l1_tem;
l2.next = l1_tem;
l2 = l2_tem;
}
}