【数据结构与算法】程序员面试金典 2.1-2.8

面试题 02.01. 移除重复节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeDuplicateNodes(ListNode* head) {
        if(head == NULL){
            return head;
        }
        set<int> si;
        ListNode *pt = head, *nt = head;
        si.insert(nt->val);
        while(nt->next != NULL){ // 下一个节点还有值
            if(si.find(nt->next->val) == si.end()){ // 且下一个节点不是重复的节点:将其值加入set,nt移动到下一个节点
                si.insert(nt->next->val);
                nt = nt->next;
            }else{ // 且下一个节点是重复的节点:跳过下一个节点,nt指向下下个节点
                nt->next = nt->next->next;
            }
        }
        return pt;
    }
};

面试题 02.02. 返回倒数第 k 个节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int kthToLast(ListNode* head, int k) {
        ListNode* fast = head, *slow = head;
        for(int i = 0; i < k; ++i){
            fast = fast->next;
        }
        while(fast != NULL){
            fast = fast->next;
            slow = slow->next;
        }
        return slow->val;
    }
};

面试题 02.03. 删除中间节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        node->val = node->next->val; // 把下个节点的值偷过来
        node->next = node->next->next; // 悄悄把毫无价值的下个节点删除
    }
};

面试题 02.04. 分割链表
答案不唯一,即使输出与所给答案不同,提交依然有可能通过整个测试。
方法:双指针
cur是遍历的当前节点,pre为遍历中第一个未交换的节点。
cur向后遍历过程中,若遇到小于x的元素,就将cur的val与pre的val进行交换;否则,就一直往下遍历,直到链表尾。
例如: [3, 5, 8, 5, 10, 2, 1] 5 -> [3, 2, 1, 5, 10, 5, 8]。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(head == NULL){
            return head;
        }
        ListNode *pre = head, *cur = head;
        while(cur != NULL){
            if(cur->val < x){
                swap(pre->val, cur->val);
                pre = pre->next;
            }
            cur = cur->next;
        }
        return head;
    }
};

面试题 02.05. 链表求和

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode * head = new ListNode(-1);
        ListNode * p1 = l1, * p2 = l2, * p = head;
        int sum = 0, carr = 0; // 和,进位

        while(p1 || p2 || carr){
            sum = 0;
            if(p1 != NULL){
                sum += p1->val;
                p1 = p1->next;
            }
            if(p2 != NULL){
                sum += p2->val;
                p2 = p2->next;
            }
            sum += carr;
            ListNode * temp = new ListNode(sum % 10);
            carr = sum / 10;
            p->next = temp;
            p = p->next;
        }
        return head->next;        
    }
};

面试题 02.06. 回文链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
    public:
    bool isPalindrome(ListNode* head) {
        // 使用快慢指针,慢指针在进行操作的时候,顺带的进行链表的翻转,在进行半个链表之间的比较
        if(head == NULL){
            return true;
        }

        ListNode* dummyNode = new ListNode(0);
        dummyNode->next = head;
        ListNode* slow = dummyNode;
        ListNode* fast = dummyNode;
        ListNode* prev = NULL;

        // 使用快慢指针找到链表的中间位置,并翻转慢指针前的链表
        while(fast != NULL && fast->next != NULL){
            fast = fast->next->next;
            // 链表翻转
            ListNode* temp = slow->next;
            slow->next = prev;
            prev = slow;
            slow = temp;
        }
        ListNode* rigth;
        if(fast != NULL){
            // 不为空,表示链表size为偶数
            // 0 -> 1 -> 2 -> 3, prev指向1,slow指向2,要比较prev与right,先确定位置1与2的val是不是一样的
            if(slow->val != slow->next->val){
                return false;
            }
            // rigth从3开始
            rigth = slow->next->next;
        }
        else{
            // 为空,表示链表size为奇数
            // 0 -> 1 -> 2 -> 3 -> 4, prev指向1,slow指向2
            // rigth从3开始
            rigth = slow->next;
        }
        // 比较两个链表
        while(prev != NULL && rigth != NULL){
            if(prev->val != rigth->val){
                return false;
            }
            prev = prev->next;
            rigth = rigth->next;
        }
        return true;
    }
};

面试题 02.07. 链表相交

翻转链表后,从后往前找到最后一个相同的节点:但是这样修改了原来链表的状态

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode * preA = headA, *curA = headA->next;
        preA->next = NULL;
        while(curA != NULL){
            ListNode* temp = curA->next;
            curA->next = preA;
            preA = curA;
            curA = temp; 
        }

        ListNode* preB = headB, *curB = headB->next;
        preB->next = NULL;
        while(curB != NULL){
            ListNode* temp = curB->next;
            curB->next = preB;
            preB = curB;
            curB = temp; 
        }

        while(preA != NULL && preB != NULL){
            if(preA != preB){
                preA = preA->next;
                preB = preB->next;
            }else{
                return preA;
            }
        }
        return NULL;
    }
};

使用set来记录链表1中的节点,再遍历链表2中的节点,与set中的数据进行比较:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set<ListNode *> sl;
        while(headA != NULL){
            sl.insert(headA);
            headA = headA->next;
        }
        while(headB != NULL){
            if(sl.find(headB) != sl.end()){ // 等价于 if(sl.count(headB) == 1){
                return headB;
            }
            headB = headB->next;
        }
        return NULL;
    }
};

这种解法太强了:
在这里插入图片描述
这题应该是比较明显的双指针题,要是能实现一种算法让两个指针分别从A和B点往C点走,两个指针分别走到C后,又各自从另外一个指针的起点,也就是A指针第二次走从B点开始走,B指针同理,这样,A指针走的路径长度 AO + OC + BO 必定等于B指针走的路径长度 BO + OC + AO,这也就意味着这两个指针第二轮走必定会在O点相遇,相遇后也即到达了退出循环的条件。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode * t1 = headA, * t2 = headB;
        while(t1 != t2){
            if(t1 != NULL){
                t1 = t1->next;
            }else{
                t1 = headB;
            }

            if(t2 != NULL){
                t2 = t2->next;
            }else{
                t2 = headA;
            }
        }
        return t1;
    }
};

面试题 02.08. 环路检测

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head == NULL || head->next == NULL){// 如果为空,或只有1个节点,必然不存在环
            return NULL;
        }
        ListNode* fast = head, * slow = head; // 准备好快慢指针
        while(fast != NULL && fast->next != NULL){// 快指针和快指针的下一个节点不为空时,才能一起行走
            fast = fast->next->next;// 快指针走两步
            slow = slow->next;// 慢指针走1步
            if(fast == slow){ // 只要有环总会相遇,如果遇到fast为空,或fast的下一个节点为空还没相遇,说明就不存在环
                break;
            }
        }
        if(fast != slow){// 如果不是因为两者相等而退出while循环,说明是因为fast或fast->next节点为空而退出while循环,表明没有环
            return NULL;
        }
        fast = head;// 到这里,说明fast = slow,此时,slow不动,让fast回到头
        while(fast != slow){// 两者一步一步,相遇的地方就是环的入口
            fast = fast->next;
            slow = slow->next;
        }
        return fast;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值