11.22-双指针
3233. 统计不是特殊数字的数字数量
思路:埃式筛选法+前缀和
思考方法,可以写一个简单的程序看看题目中提到的特殊数字有没有什么规律:
正难则反,统计区间 [l,r] 内有多少个特殊数字==统计[l , r]内有多少质数的平方。
这等价于区间 [0,r] 内的特殊数字个数,减去区间 [0 , l−1] 内的特殊数字个数。(想到前缀和)
根据题意,只有质数的平方 \(p ^2\) 才是特殊数字,因为 \(p ^2\) 恰好有两个真因数 1 和 p。而其他的数,1 没有真因数,质数只有 1 个真数,不是 1 不是质数也不是质数平方的数有至少三个真因数。
所以区间 [0,i] 内的特殊数字个数等于:区间 [ 0,\(\sqrt{i}\)] 中的质数个数。
埃式筛选法:
每次从i*i开始,去掉当前i的倍数,标记为合数(-1)。
预处理 \(⌊ \sqrt{10^9}⌋\)=31622 内的质数,然后用前缀和计算 [0,i] 中的质数个数 π(i),那么区间 [l,r] 内的特殊数字个数就是\(π(\lfloor r \rfloor)−π(\lfloor l−1 \rfloor)\)
答案为区间内的整数个数,减去区间内的特殊数字个数,即\(r - l + 1 -(π(\lfloor r \rfloor)−π(\lfloor l−1 \rfloor))\)
const int MX = 31622;
int pi[MX + 1];
auto init = [] {
for (int i = 2; i <= MX; i++) {
if (pi[i] == 0) { // i 是质数
pi[i] = pi[i - 1] + 1;
for (int j = i * i; j <= MX; j += i) { // 注:如果 MX 比较大,小心 i*i 溢出
pi[j] = -1; // 标记 i 的倍数为合数
}
} else {
pi[i] = pi[i - 1];
}
}
return 0;
}();
class Solution {
public:
int nonSpecialCount(int l, int r) {
return r - l + 1 - (pi[(int) sqrt(r)] - pi[(int) sqrt(l - 1)]);
}
};
328. 奇偶链表
思路:模拟
定义奇数指针even=head,偶数指针odd=head->next,当然先判断特殊情况空链表和1个节点的情况。
然后当even后面至少还有两个节点(后面至少还有一个奇节点时)循环进行。
even跳一个:even->next = odd->next;
odd也跳一个: odd->next = even->next->next;//当然这里可能为null
然后依次后移,最后把记下的偶数头odd1接到even后。
class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
if(!head || !head->next) return head;
ListNode* even = head;
ListNode* odd = head->next;
ListNode* odd1 = head->next;
while(even && even->next && even->next->next){
even->next = odd->next;
odd->next = even->next->next;
even = even->next;
odd = odd->next;
}
even->next = odd1;
return head;
}
};
86. 分隔链表
思路:
考虑通过「新建两个链表」实现原链表分割,算法流程为:
- 新建两个链表
sml_dummy,big_dummy,分别用于添加所有「节点值 <x 」、「节点值 ≥x 」的节点。- 遍历链表 head 并依次比较各节点值 head.val 和 x 的大小:
若head.val < x,则将节点 head 添加至链表sml_dummy最后面;
若head.val >= x,则将节点 head 添加至链表big_dummy最后面;- 遍历完成后,拼接
sml_dummy和big_dummy链表。- 最终返回头节点
sml_dummy.next即可。
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* smlDummy = new ListNode(0);
ListNode* bigDummy = new ListNode(0);
ListNode *s = smlDummy , *b = bigDummy , *cur = head;
while(cur){
if(cur->val < x) {
s->next = cur;
s = s->next;
}
else {
b->next = cur;
b = b->next;
}
cur = cur->next;
}
s->next = bigDummy->next;
b->next = NULL;
return smlDummy->next;
}
};
160. 相交链表
思路:先走长度差步
求出A、B的链表长度n1、n2,保证n1 < n2(如果不是就交换),然后让curB提前走
gap=n2 - n1步,然后curA与curB一起走,每步判断节点是否相同。
class Solution {
private:
int lenth(ListNode* head){
ListNode* cur = head;
int n = 0;
while(cur){
n ++;
cur = cur->next;
}
return n;
}
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int n1 = lenth(headA) , n2 = lenth(headB);
if(n1 >= n2){
swap(headA , headB);
swap(n1 , n2);
}
int gap = n2 - n1;
ListNode* curA = headA;
ListNode* curB = headB;
while(gap --) curB = curB->next;
while(curA != curB){
curA = curA->next;
curB = curB->next;
}
return curA;
}
};
另解:两表拼接消除长度差
设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a ,「链表 headB」的节点数量为 b ,「两链表的公共尾部」的节点数量为 c ,则有:
头节点 headA 到 node 前,共有 a−c 个节点;头节点 headB 到 node 前,共有 b−c 个节点;
考虑构建两个节点指针 A , B 分别指向两链表头节点 headA , headB ,做如下操作:
指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:
a+(b−c)指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:
b+(a−c)如下式所示,此时指针 A , B 重合,并有两种情况:
a+(b−c)=b+(a−c)
- 若两链表 有 公共尾部 (即 c>0 ) :指针 A , B 同时指向「第一个公共节点」node 。
- 若两链表 无 公共尾部 (即 c=0 ) :指针 A , B 同时指向 null 。
因此返回 A 即可。代码解释:为什么把链表连起来,就可以得到相交的部分。
首先是两个链表(约定,值相同代表同一节点,0 代表空节点) A表:[1, 2, 3, 7, 8, 9] B表:[4, 5, 7, 8, 9]
连接两个链表(表与表之间用 0 隔开)
AB表:[1, 2, 3, 7, 8, 9, 0, 4, 5, 7, 8, 9, 0]
BA表:[4, 5, 7, 8, 9, 0, 1, 2, 3, 7, 8, 9, 0]
观察连接后的两个表,可以发现相交的部分整齐的排列在末尾, 只需要逐个比较这两张表的节点,就能找到相交的起始位置。
如果没有相交会如何?会陷入死循环吗? A表:[1, 2, 3] B表:[4, 5] 连接两个链表(表与表之间用 0 隔开) AB表:[1, 2, 3, 0, 4, 5, 0] BA表:[4, 5, 0, 1, 2, 3, 0] 观察连接后的两个表,可以发现末尾相交的部分必然为空, 参照上面的逻辑,返回首个相同的节点,为空是符合题意的。
如果连接两表时,不用 0 隔开,表不相交时,就会陷入死循环。 但是写代码时,不可能往链表中插入空节点,所以就用一个指针,模拟遍历两个相交表的过程,当指针指向空时,重新指向另一个链表的头节点,否则就指向下一个节点。 思考一下,如果两个链表长度相等会如何? 可以试着在纸上画一下~
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *A = headA, *B = headB;
while (A != B) {
A = A != nullptr ? A->next : headB;//走完A表之后开始遍历B,相当于把A、B连起来
B = B != nullptr ? B->next : headA;
}//最后如果A、B都走完必然都为空,跳出循环返回NULL。
return A;
}
};




浙公网安备 33010602011771号