前言
暑假临近结束,自己也没有了学习的动力,每天只是打打酱油似的做了几道题,而且还迷上了吃鸡,疯狂和同学在网吧相约吃鸡,本来想着早点去学校调整调整状态的,结果被告知宿舍只能在开学的时候入住。闲着没事,干脆就学学习吧。
LintCode Partition List
给定一个链表L和一个数x,让你把这个链表排序,把值小于x的节点放在前面,值大于等于x的节点放在后面,要求各节点之间保持原有的顺序。
思路
可以声明一个左链表头节点和右链表头节点,遍历原有链表一遍,把小于x的节点按顺序接在左链表后面,其余的接在右链表后面,最后把左右链表首尾拼接在一起即可。
注意
本题中向链表中插入节点的方法属于尾插法。在测试样例中发现,给定的链表head节点不是头节点而是第一个节点(因为链表往往有一个头节点,其值域是没有意义的)。同理最后返回的节点也是第一个节点而非头节点。
代码
ListNode * partition(ListNode * head, int x) {
// write your code here
ListNode *lefthead = new ListNode;
ListNode *righthead = new ListNode;
ListNode *lefttail = lefthead;
ListNode *righttail = righthead;
while (head) {
if (head->val < x) {
lefttail->next = head;
lefttail = head;
} else {
righttail->next = head;
righttail = head;
}
head = head->next;
}
lefttail->next = righthead->next;
righttail->next = NULL;
return lefthead->next;
}
LintCode Remove Nth Node From End of List
删除给定链表的倒数第N个节点,要求不能求得链表的长度。
思路
先找到整数第k个节点,然后用两个指针分别指向该节点和头结点,并同时往后遍历,当后面那个指针便利到尾节点时,前面那个指针就到达了倒数第k个节点,只需要将其删除即可。
注意
删除一个节点需要保存其头结点的信息。这也是链表的基本功。
代码
ListNode *removeNthFromEnd(ListNode *head, int n) {
// write your code here
ListNode *p, *q, *pre, *res;
pre = new ListNode(0);
pre->next = head;
p = head, q = p, res = pre;
while (n--) q = q->next;
while (q) {
q = q->next;
pre = p;
p = p->next;
}
pre->next = p->next;
free(p);
//free的结果是p正在指向的地址没有变,但此时在该地址处的数据已经没有定义了。
return res->next;
}
LintCode Interleaving Positive and Negative Numbers
给定一个由正负数组成的序列,让你把他们变成正负交错的序列,不需要维持原有序列的顺序,但是要求do it in-place.
思路
首先遍历一遍序列,统计是正数多还是负数多以决定第一个开始的数是正还是负,然后用两个指针分别指向第一个正数还是负数,然后遍历原序列即可,不断将其交换即可。
代码
void rerange(vector<int> &A) {
// write your code here
int posi = 0, nega = 0;
int cnt1 = 0, cnt2 = 0;
for (auto n: A) {
if (n > 0) cnt1++;
else cnt2++;
}
bool tag = cnt1 <= cnt2;
int i = 0;
while (i < A.size()) {
if (tag) {
while (nega < A.size() && A[nega] > 0) nega++;
if (nega != A.size()) swap(A[nega++], A[i]);
} else {
while (posi < A.size() && A[posi] < 0) posi++;
if (posi != A.size()) swap(A[posi++], A[i]);
}
i++;
tag = !tag;
}
}
LintCode Sort ColorsII
给定一个大小为n的数组,其由数1~k组成(k <= n),现在让你把这个数组按照1~k的顺序排序,要求不要用库sort函数。
思路
一个简单的思路是使用计数排序,即先遍历一遍统计各个数字出现了多少次,然后再按照这个次数重新修改原序列。
如果要求不使用额外的空间呢?
也可以用上述的思路,不过我们用原数组来存储各个数组出现了多少次。依旧是遍历原数组,如果遍历到位置i,其上数c[i],那么我们用c[c[i]-1]来统计次数。为了不与原数组冲突,我们用负数来代表出现的次数。那么如果c[i]为负数,就跳过;c[c[i]-1]为正数,则把c[c[i]-1]与c[i]交换,然后把c[c[i]]置成-1,接着再从i开始判断。如果c[c[i]-1]为负数,则再其基础上减1,代表出现的次数又多了一次,然后把colors[i]置为0,表示这个地方已经计算过。
遍历完了之后再从数组的末尾把所得到的结果填进去。
代码
void sortColors2(vector<int> &colors, int k) {
// write your code here
for (int i = 0; i < colors.size();) {
if (colors[i] <= 0) {
i++;
continue;
}
else if (colors[colors[i] - 1] >= 0) {
int tmp = colors[i] - 1;
colors[i] = colors[tmp];
colors[tmp] = -1;
}
else {
colors[colors[i] - 1]--;
colors[i] = 0;
i++;
}
}
int index = k - 1;
int j = colors.size() - 1;
while (j >= 0) {
int cnt = -colors[index];
while (cnt > 0) {
colors[j--] = index+1;
cnt--;
}
index--;
}
}
LintCode Traingle Count
给定一个数组,从中任选三个数,可以组成三角形,问一共有多少种取法。
思路1
枚举最大、最小边,然后利用二分法寻找第一个大于(最大边-最小边)的数,那么从这个数开始一直到r都是满足题意的。复杂度为二重循环里套了一个二分,因此为O(n2logn)。
注意1
二分法求第一个大于某数的位置。
代码1
int triangleCount(vector<int> S) {
// write your code here
int res = 0;
sort(S.begin(), S.end());
for (int i = 0; i < S.size(); i++) {
for (int j = S.size()-1; j > i+1; j--) {
int l = i+1, r = j;
int target = S[j] - S[i];
while (l < r) {
int mid = (l+r) >> 1;
if (S[mid] <= target) l = mid+1;
else r = mid;
}
res += j - l;
}
}
return res;
}
思路2
由于将给定的序列进行排序便于处理,然后我们发现就是判断S[i]+S[j] > S[k]。那么先固定k,然后在0~k-1范围内从两头开始寻找i和j就很简单了。注意如果发现了一个j,那么i~j-1的数都是满足题意的。
代码2
int triangleCount(vector<int> S) {
// write your code here
int res = 0;
sort(S.begin(), S.end());
for (int k = S.size()-1; k >= 2; k--) {
int i = 0, j = k-1;
while (i < j) {
if (S[i] + S[j] > S[k]) {
res += j-i;
j--;
} else
i++;
}
}
return res;
}