数据结构—单链表OJ

640?wx_fmt=gif

目录

Number 1:删除链表中等于给定值 val 的所有结点。

Number 2:反转一个单链表。

Number 3:给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则 返回第二个中间结点。

Number 4: 输入一个链表,输出该链表中倒数第k个结点。

Number 5:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。

Number  6:编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。

Number 7:链表的回文结构。

Number 8:输入两个链表,找出它们的第一个公共结点。

Number 9:给定一个链表,判断链表中是否有环。

Number 10:给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL


Number 1:删除链表中等于给定值 val 的所有结点。

203. 移除链表元素 - 力扣(LeetCode)https://leetcode.cn/problems/remove-linked-list-elements/description/

struct ListNode* removeElements(struct ListNode* head, int val) {

	struct ListNode* cur = head;
	struct ListNode* prev = NULL;
	while (cur)
	{
		if (cur->val == val)
		{
            //头删
			if (cur == head)
			{
				head = cur->next;
				free(cur);
				cur = head;
			}
			else
			{
				prev->next = cur->next;
				free(cur);
				cur = prev->next;
			}
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}

	return head;
}

struct ListNode* removeElements(struct ListNode* head, int val){
	struct ListNode* cur = head;
	struct ListNode* tail = NULL;
	head = NULL;
	while (cur)
	{
		if (cur->val == val)
		{
			struct ListNode* del= cur;
			cur = cur->next;
			free(del);
		}
		else
		{
			if (tail == NULL)
			{
				head = tail = cur;
			}
			else
			{
				tail->next = cur;
				tail = tail->next;
			}
			cur = cur->next;
		}

	}
	if (tail != NULL)
	{
		tail->next = NULL;
	}

	return head;
}

struct ListNode* removeElements(struct ListNode* head, int val) {

	struct ListNode* cur = head;
	struct ListNode* tail = NULL;
	head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
	tail->next = NULL;
	while (cur)
	{
		if (cur->val == val)
		{
			struct ListNode* del = cur;
	    	cur = cur->next;
			free(del);
		}
		else
		{
			tail->next = cur;
			tail = tail->next;	
		    cur = cur->next;
		}
	}
	tail->next = NULL;
	struct ListNode* del = head;
	head = head->next;
	free(del);
	return head;
}

Number 2:反转一个单链表。

206. 反转链表 - 力扣(LeetCode)https://leetcode.cn/problems/reverse-linked-list/submissions/

struct ListNode* reverseList(struct ListNode* head){

	struct ListNode* prev = NULL;
	struct ListNode* next = NULL;
	while (head != NULL)
	{
		next = head->next;
		head->next = prev;
		prev = head;
		head = next;
	}
	return prev;
}

Number 3:给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则 返回第二个中间结点。

876. 链表的中间结点 - 力扣(LeetCode)https://leetcode.cn/problems/middle-of-the-linked-list/submissions/

struct ListNode* middleNode(struct ListNode* head){
	struct ListNode* slow = head;
	struct ListNode* fast = head;
	while (fast != NULL && fast->next!=NULL)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}

struct ListNode* middleNode(struct ListNode* head) {
	int count = 0;
	int mid = 0;
	struct ListNode* cur = head;
	while (cur != NULL)
	{
		count++;
		cur = cur->next;
	}
	mid = count / 2 + 1;
	while (--mid)
	{
		head = head->next;
	}
	return head;
}

Number 4: 输入一个链表,输出该链表中倒数第k个结点。

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&&tqId=11167&rp=2&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // write code here
	struct ListNode* slow = pListHead;
	struct ListNode* fast = pListHead;
	//当fast提前走k步
	while(k--)
	{
		if(fast == NULL)
			return NULL;
		fast = fast->next;
	}
	while(fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	
	return slow;
}

Number 5:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。

21. 合并两个有序链表 - 力扣(LeetCode)https://leetcode.cn/problems/merge-two-sorted-lists/description/

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }
    struct ListNode* head = NULL;
    struct ListNode* tail = NULL;

    while(list1 && list2)
    {
        if(list1->val < list2->val)
        {
            if(tail == NULL)
            {
                head = tail = list1;
            }
            else
            {
                tail->next = list1;
                tail =tail->next;
            }
            list1 = list1->next;
        }
        else
        {
            if(tail == NULL)
            {
                head = tail = list2;
            }
            else
            {
                tail->next = list2;
                tail =tail->next;
            }
            list2 = list2->next;            
        }
    }
    if(list1 != NULL)
    {
        tail->next = list1;
    }
    if(list2 != NULL)
    {
        tail->next = list2;
    }
    return head;

}

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
 
    struct ListNode* head = NULL;
    struct ListNode* tail = NULL;
    head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
    tail->next = NULL;
    while(list1 && list2)
    {
        if(list1->val < list2->val)
        {
    
            tail->next = list1;
            tail =tail->next;
            list1 = list1->next;
        }
        else
        {
             tail->next = list2;
             tail =tail->next;
            list2 = list2->next;            
        }
    }
    if(list1 != NULL)
    {
        tail->next = list1;
    }
    if(list2 != NULL)
    {
        tail->next = list2;
    }
    struct ListNode* del = head;
    head = head ->next;
    free(del);
    return head;

}

Number  6:编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。

链表分割_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking

思路1:定义两个带有哨兵位的头结点,lesshead,greaterhead分别存储大小数据

同时使用两个指针lesstail、greatertail给给进行尾插,最后合并两个链表

lesstail->next = greaterhead;

返回合并链表的头结点,并且释放两个哨兵位结点

注意:

1.分析极端场景,脱离测试用例

2.分析,思考极端边界

3.第一个,都比x小,第二个,都比x大,第三个,有大有小

        1.都比x大,此时lesshead->next = lesstail->next = NULL,此时,greatertail->next = NULL,而lesshead->next = lesstail->next = greaterhead->next 链接表时,不带哨兵位的头结点为greaterhead->next ,可以返回lesshead->next

        2.都比x小,此时greaterhead->next = greatertail->next = NULL,此时,lesstail->next = NULL,而lesstail->next = greaterhead->next链接表时,不带哨兵位的头结点为lesshead->next,可以返回lesshead->next

4.有大有小,又分为两种情况,

        第一种情况:geatertail最后比x大,greatertail->taill == NULL,此时lesstail->next 可以直接链接greaterhead

        第二种情况:geatertail最后比x小,此时greatertail-> != NULL,此时会形成循环链表,因此此情况greatertail单链表尾必须要置为空

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
  public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode* greaterhead, *greatertail, *lesshead, *lesstail;
        greaterhead = greatertail = (struct ListNode*)malloc(sizeof(struct ListNode ));
        lesshead = lesstail = (struct ListNode*)malloc(sizeof(struct ListNode ));
        lesstail->next = NULL;
        greaterhead ->next = NULL;

        struct ListNode* cur = pHead;
        while (cur) {
            if (cur->val < x) {
                lesstail->next = cur;
                lesstail = lesstail->next;
            } else {
                greatertail->next = cur;
                greatertail = greatertail->next;
            }
            cur = cur->next;
        }
        greatertail->next = NULL;
        lesstail->next = greaterhead->next;
        struct ListNode* head = lesshead->next;
        free(greaterhead);
        free(lesshead);
        return head;
    }
};

Number 7:链表的回文结构。

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking

对称就是回文

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

通过查找中间结点,逆置,判断

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        vector<int> vals;
        while (head != nullptr) {
            vals.emplace_back(head->val);
            head = head->next;
        }
        for (int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j) {
            if (vals[i] != vals[j]) {
                return false;
            }
        }
        return true;
    }
};
 
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
  public:
    struct ListNode* middleNode(struct ListNode* head) {
        struct ListNode* slow = head;
        struct ListNode* fast = head;
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }

    struct ListNode* reverseList(struct ListNode* head) {
        struct ListNode* cur = head;
        struct ListNode* prev = NULL;
        struct ListNode* next = NULL;
        while (head != NULL) {
            next = head->next;
            head->next = prev;
            prev = head;
            head = next;
        }
        return prev;
    }
    bool chkPalindrome(ListNode* A) {
        struct ListNode* head = A;
        struct ListNode* mid = middleNode(head);
        struct ListNode* rhead = reverseList(mid);
        
        while(head && rhead)
        {
            if(head->val != rhead->val)
            {
                return false;
            }
            else
            {
                head = head->next;
                rhead = rhead->next;
            }
        }
        return true;
    }
};

Number 8:输入两个链表,找出它们的第一个公共结点。

160. 相交链表 - 力扣(LeetCode)https://leetcode.cn/problems/intersection-of-two-linked-lists/description/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA == NULL || headB == NULL)
    {
        return NULL;
    }
    struct ListNode* curA = headA;
    struct ListNode* curB = headB;
    int lenA = 1;
    int lenB = 1;
    while(curA->next)
    {
       curA = curA->next;
       lenA++;
    }
    while(curB->next)
    {
       curB = curB->next;
       lenB++;
    }
    if(curA!=curB)
    {
        return NULL;
    }
    struct ListNode* shortList = headA,*longList = headB;
    if(lenA>lenB)
    {
        shortList = headB;
        longList = headA;
    }
    int gap = abs(lenA - lenB);
    while(gap--)
    {
        longList = longList->next;
    }
    while(shortList != longList)
    {
        shortList = shortList->next;
        longList = longList->next;
    }
    return shortList;
}

Number 9:给定一个链表,判断链表中是否有环。

Loading Question... - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle/description/【思路】

快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行, 如果链表 带环则一定会在环中相遇,否则快指针率先走到链表的末尾。比如:陪女朋友到操场跑步减肥。

【扩展问题】

为什么快指针每次走两步,慢指针走一步可以?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。

当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。

此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情 况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

快指针一次走3步,走4步,...n步行吗?

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* fast =head,*slow=head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return true;
        }
    }
    return false;
}

Number 10:给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL

142. 环形链表 II - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle-ii/description/结论:

让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环 运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。

证明:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast =head,*slow=head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
           struct ListNode*  meet = slow;
           while(meet != head)
           {
               head = head->next;
               meet = meet->next;
           }
           return meet;
        }
    }
    return false;
}

链表OJ题目链接:

链表知识点题库 - 力扣(LeetCode)https://leetcode.cn/tag/linked-list/problemset/牛客网在线编程_编程学习|练习题_数据结构|系统设计题库 (nowcoder.com)https://www.nowcoder.com/exam/oj

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值