LintCode 解题记录 17.8.30 两个指针

前言

暑假临近结束,自己也没有了学习的动力,每天只是打打酱油似的做了几道题,而且还迷上了吃鸡,疯狂和同学在网吧相约吃鸡,本来想着早点去学校调整调整状态的,结果被告知宿舍只能在开学的时候入住。闲着没事,干脆就学学习吧。

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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值