day1 链表专题 牛客TOP100 BM 1-10

文章目录

  • 链表
    • BM1 反转链表
    • BM2 链表内指定区间反转
    • BM3 链表中的节点每k个一组翻转
    • BM4 合并两个排序的链表
    • BM5 合并k个已排序的链表
    • BM6 判断链表中是否有环
    • BM7 链表中环的入口结点
    • BM8 链表中倒数最后k个结点
    • BM9 删除链表的倒数第n个节点
    • BM10 两个链表的第一个公共结点

链表

BM1 反转链表

题目
day1 链表专题 牛客TOP100 BM 1-10_第1张图片

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* ReverseList(ListNode* head) {
        if(head == nullptr) return head;
        ListNode* cur = head;
        ListNode* pre = nullptr;
        ListNode* temp;
        while(cur)
        {
            temp = cur->next;//保存cur下一个节点
            cur->next = pre;//反转
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

BM2 链表内指定区间反转

day1 链表专题 牛客TOP100 BM 1-10_第2张图片
自己写的时候,思路正确,但是处理不好反转后的拼接

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        if(m == n) return head;
        ListNode* dummyhead = new ListNode(-1);
        dummyhead->next = head;

        //反转链表的前部分
        ListNode* start = dummyhead;
        for(int i=1; i<m; i++)
            start = start->next;//结点1
        cout << "start: "<< start->val <<endl;
        
        //反转链表的尾巴 第n个结点
        ListNode* right = start->next;
        for(int i=m; i<n; i++)
            right = right->next;//结点4
        cout << "right: "<< right->val <<endl;

        //反转链表的头尾
        ListNode* left = start->next;//2
        ListNode* end = right->next;//5
        
        //切断 left 1 2 3 4
        start->next = nullptr;
        right->next = nullptr;
        reverlist(left);

        //接回原来的链表
        start->next = right;//1->4
        left->next = end;//2->5
        return dummyhead->next;
    }
    void reverlist(ListNode* node)
    {
        ListNode* pre = nullptr;
        ListNode* cur = node;
        ListNode* temp;
        while(cur)
        {
            temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
    }
};

写法2

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @param m int整型 
     * @param n int整型 
     * @return ListNode类
     */
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        if(m == n) return head;
        ListNode* dummyhead = new ListNode(-1);
        dummyhead->next = head;

        //写法1
        /*//反转链表的前部分
        ListNode* start = dummyhead;
        for(int i=1; inext;//结点1
        cout << "start: "<< start->val <next;
        for(int i=m; inext;//结点4
        cout << "right: "<< right->val <next;//2
        ListNode* end = right->next;//5
        
        //切断 left 1 2 3 4
        start->next = nullptr;
        right->next = nullptr;
        reverlist(left);

        //接回原来的链表
        start->next = right;//1->4
        left->next = end;//2->5
        return dummyhead->next;*/

        //写法2
        ListNode* pre = dummyhead;
        ListNode* cur = head;
        
        //找到结点m
        for(int i=1; i<m; i++)
        {
            pre = cur;//m-1
            cur = cur->next;//m
        }
        //从m到n反转 依次断掉指向后续的指针,反转指针方向
        ListNode* temp;
        for(int i=m; i<n; i++)
        {
            temp = cur->next;//要反转的结点
            cur->next = temp->next;//指向n
            temp->next = pre->next;//反转
            pre->next = temp;//指向m
        }
        return dummyhead->next;

    }
    void reverlist(ListNode* node)
    {
        ListNode* pre = nullptr;
        ListNode* cur = node;
        ListNode* temp;
        while(cur)
        {
            temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
    }
};

BM3 链表中的节点每k个一组翻转

day1 链表专题 牛客TOP100 BM 1-10_第3张图片
自己写的乱七八糟,看了解答之后觉得好聪明啊,关键是要找到反转前的局部链表的尾巴,这个也是要返回的链表头,然后建立每一组的连接!!

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        //每一组局部链表的表头 反转前局部链表的尾巴 
        ListNode* tail = head;
        //找到尾巴
        for(int i=0; i<k; i++)
        {
            //如果链表不够长 返回结果
            if(tail == nullptr) return head;
            tail = tail->next;
        }

        //反转局部链表
        ListNode* pre = nullptr;
        ListNode* cur = head;
        ListNode* temp;
        //在到达当前段尾节点前
        while(cur != tail)
        {
            temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }

        //反转后链表尾部连接下一组表头
        head->next = reverseKGroup(tail, k);
        return pre;
    }
};

模拟的写法,将一条链表分为链表长度/k块链表,如果处不尽则说明后面会有剩下的那一块是不满长度为k的。
最初需要定义虚拟表头dummyhead(最终结果)和局部链表的反转前的表头start。然后遍历每一组局部链表,并反转。反转后需要将start与反转后的局部链表头pre连接(反转前局部链表的尾部),再更新下一组的局部链表的表头,也就是将start更新到最前。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(k<=1) return head;
        if(head == nullptr) return nullptr;

        int len = getLength(head);
        int part = len / k;//分组

        ListNode* dummyhead = new ListNode(-1);
        ListNode* start = dummyhead;//每一组局部链表的表头
        for(int i=0; i<part; i++)
        {
            //局部链表反转后的尾巴
            ListNode* pre = nullptr;
            for(int j=0; j<k; j++)
            {
                ListNode* temp = head->next;
                head->next = pre;
                pre = head;
                head = temp;
            }
            start->next = pre;//链表头连接 反转后的局部链表 的表头
            while(start->next) start = start->next;//更新下一组的表头
        }
        start->next = head;
        return dummyhead->next;

    }
private:
    //获取链表长度
    int getLength(ListNode* node)
    {    
        int len = 0;
        if(node == nullptr) return len;
        while(node)
        {
            len++;
            node = node->next;
        }
        return len;
    }
};

BM4 合并两个排序的链表

day1 链表专题 牛客TOP100 BM 1-10_第4张图片

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        if(!pHead1 && !pHead2) return nullptr;
        if(!pHead1) return pHead2;
        if(!pHead2) return pHead1;

        ListNode* dummyhead = new ListNode(-1);
        ListNode* cur = dummyhead;
        while(pHead1 && pHead2)
        {
            if(pHead1->val <= pHead2->val)
            {
                cur->next = pHead1;
                pHead1 = pHead1->next;//1->2
                //cout << "   cur:" << cur->next->val << "  pHead1:" << pHead1->val << endl;
            }
            else if(pHead1->val > pHead2->val)
            {
                cur->next = pHead2;
                pHead2 = pHead2->next;
                //cout << "   cur:" << cur->next->val << "  pHead2:" << pHead2->val << endl;
            }
            cur = cur->next;//连接新链表
        }
        //如果还有剩下的结点 单独处理最后一个结点
        cur->next = (pHead2 == nullptr) ? pHead1 : pHead2;
        return dummyhead->next;
    }
};

方法2,取较小的结点

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        if(!pHead1 && !pHead2) return nullptr;
        if(!pHead1) return pHead2;
        if(!pHead2) return pHead1;
        ListNode* dummyhead = new ListNode(-1);
        ListNode* cur = dummyhead;
        while(pHead1 && pHead2)
        {
            if(pHead1->val > pHead2->val) swap(pHead1, pHead2);
            cur->next = pHead1;//建立新连接
            pHead1 = pHead1->next;//更新
            cur = cur->next;//更新
        }
        cur->next = (pHead2 == nullptr) ? pHead1 : pHead2;
        return dummyhead->next;
    }
};

BM5 合并k个已排序的链表

day1 链表专题 牛客TOP100 BM 1-10_第5张图片

这个写法会把负数的结点吞掉,例如输入:[{-5},{-9,-8,-7,-5,1,1,1,3},{-10,-7,-6,-6,-6,0,1,3,3},{-10,-8,-7,-2,3,3},{-1,4},{-5,-4,-1}],输出是{-5,0,1,1,1,1,3,3,3,3,3,4}

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        // write code here
        ListNode* dummyhead = new ListNode(-1);
        dummyhead->next = lists[0];
        ListNode* cur = dummyhead;
        for(int i=1; i<lists.size();)
        {
            cur->next = mergeList(lists[i], lists[i+1]);
            cur = cur->next;
            i += 2;
        }
        return dummyhead->next;
    }
    ListNode* mergeList(ListNode* head1, ListNode* head2)
    {
        if(head1 == nullptr) return head2;
        if(head2 == nullptr) return head1;
        ListNode* dummyhead =  new ListNode(0);
        ListNode* cur = dummyhead;
        while(head1 && head2)
        {
            if(head1->val >= head2->val) swap(head1, head2);
            cur->next = head1;
            head1 = head1->next;
            cur = cur->next;
        }
        cur->next = (head1 == nullptr) ? head2 : head1;
        return dummyhead->next;
    }

};

加入并归排序后的,可以输出负数结点了

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        return divideMerge(lists, 0, lists.size()-1);
    }
    //划分合并区间函数
    ListNode* divideMerge(vector<ListNode *> &lists, int left, int right){
        if(left > right)
            return NULL;
        //中间一个的情况
        else if(left == right)
            return lists[left];
        //从中间分成两段,再将合并好的两段合并
        int mid = (left + right) / 2;
        return mergeList(divideMerge(lists, left, mid), divideMerge(lists, mid + 1, right));
    }
    ListNode* mergeList(ListNode* head1, ListNode* head2)
    {
        if(!head1 && !head2) return nullptr;
        if(head1 == nullptr) return head2;
        if(head2 == nullptr) return head1;
        ListNode* dummyhead =  new ListNode(0);
        ListNode* cur = dummyhead;
        while(head1 && head2)
        {
            if(head1->val >= head2->val) swap(head1, head2);
            cur->next = head1;
            head1 = head1->next;
            cur = cur->next;
        }
        cur->next = (head1 == nullptr) ? head2 : head1;
        return dummyhead->next;
    }

};

BM6 判断链表中是否有环

判断给定的链表中是否有环。如果有环则返回true,否则返回false。

思路就是,快慢指针同时出发,慢指针走一步,快指针走两步。快指针肯定先进入环,慢指针后入环,如果两个指针相遇说明有环,如果没有相遇,遍历结束说明没有换。

第一次写的时候,有一组很长的链表没有通过,while循环内没有对fast的下一个结点进行判断,可能会一直在循环里边不终止。加了一个判断,如果fast有下一个结点才更新,否则也是说明链表走到头了。又或者是while的判断条件加上fast->next != null

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head == nullptr || head->next == nullptr) return false;
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast && slow)
        {
            slow = slow->next;
            //下面这个if-else是改正后的写法
            if(fast->next) fast = fast->next->next;
            else return false;
            if(fast == slow) return true;
        }
        return false;//如果fast先遇到null说明没有环
    }
};

BM7 链表中环的入口结点

day1 链表专题 牛客TOP100 BM 1-10_第6张图片
上一题是判断有没有环,这个是有环然后要找到环入口。

思路,也是快慢指针。

  • 首先,快慢指针同时出发,慢指针走一步,快指针走两步。快指针肯定先进入环,慢指针后入环,如果两个指针相遇说明有环,如果没有相遇,遍历结束说明没有环。
  • 然后,第一次相遇时,在这个地方,重新定义两个指针,慢指针从头开始走,快指针在换里边走同时移动一步,如果相遇就找到了环入口,否则返回null。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        if(pHead == nullptr) return nullptr;
        ListNode* fast = pHead;
        ListNode* slow = pHead;
        while(fast!=nullptr && fast->next != nullptr)
        {
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast)
            {
                slow = pHead;
                ListNode* entrynode = fast;
                while(entrynode)
                {
                    //先判断 有可能就是一个闭环链表
                    if(entrynode == slow) return entrynode;
                    slow = slow->next;
                    entrynode = entrynode->next;
                }
            }
        }
        return nullptr;
    }
};

BM8 链表中倒数最后k个结点

day1 链表专题 牛客TOP100 BM 1-10_第7张图片
思路-快慢指针:

  • 首先,快指针先走K步,在这里要注意两个特殊情况,一个是链表不够长,一个是正好需要返回倒数最后一个结点(第一个结点)。
  • 然后,慢指针从头开始,和快指针同时前进,快指针走到链表尾,慢指针走到第k个结点了。
/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 *  ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
  public:
    ListNode* FindKthToTail(ListNode* pHead, int k) {
        //空链表
        if (pHead == nullptr) return pHead;
        ListNode* fast = pHead;
        while(k--)
        {
            if(fast->next == nullptr && k>0) return nullptr;//链表不够长
            if(fast->next == nullptr) return pHead;//倒数最后一个
            fast = fast->next;
            //cout << "  fast: " << fast->val;
        }
        while(fast)
        {
            pHead = pHead->next;
            fast = fast->next;
        }
        return pHead;
    }
};

BM9 删除链表的倒数第n个节点

day1 链表专题 牛客TOP100 BM 1-10_第8张图片
思路在上一题的基础上,在保存一个倒数第K-1个节点,然后逻辑删除就可以了。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(head == nullptr) return head;
        ListNode* fast = head;
        while(n--)
        {
            if(fast->next==nullptr && n>0) return nullptr;
            if(fast->next==nullptr)
            {
                return head->next;//逻辑删除
            }
            cout << "  fast: " << fast->val;
            fast = fast->next;
        }
        ListNode* slow = head;
        ListNode* temp;
        while(fast)
        {
            temp = slow;//倒数第k-1个结点
            slow = slow->next;//倒数第k个结点
            fast = fast->next;
        }
        temp->next = slow->next;
        return head;
    }
};

BM10 两个链表的第一个公共结点

day1 链表专题 牛客TOP100 BM 1-10_第9张图片
思路:统计长度,计算长度差len,长的链表先走len步,然后再和短链表一起走,如果两结点相同说明有公共结点。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if(!pHead1 || !pHead2) return nullptr;
		int len1 = getLenofList(pHead1);
		int len2 = getLenofList(pHead2);
		if(len1 < len2)
		{
			swap(pHead1, pHead2);
			swap(len1, len2);	
		}
		int len = len1 - len2;
		while(len--)
		{
			pHead1 = pHead1->next;
		}
		//cout << pHead1->val<
		while(pHead1)
		{
			if(pHead1 == pHead2) return pHead1;
			pHead1 = pHead1->next;
			pHead2 = pHead2->next;
		}
		return nullptr;
    }
	int getLenofList(ListNode* head)
	{
		int res = 0;
		if(head == nullptr) return res;
		while(head)
		{
			res++;
			head = head->next;
		}
		return res;
	}
};

你可能感兴趣的:(c++)