初级算法之链表

总结:链表问题除了添加节点,删除节点,查找节点等常规操作,还要关注一种方法:双指针法,双指针的应用例如题目4;而在双指针法中又可以细化出一种方法:快慢指针,题目5,题目6均是快慢指针的实际应用,而题目2其实也可以使用快慢指针去解决,所以快慢指针是一种很重要的解决链表问题的方法。

题目1:删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

现有一个链表 -- head = [4,5,1,9],它可以表示为:

示例 1:

输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

思路:将node后一个节点的值赋给node节点,再将node后一个节点的指针域赋给node节点,这样等于用node后一个节点替换了node,做到了对node的删除。

void deleteNode(ListNode* node) 
{
	node->val = node->next->val;
	node->next = node->next->next;
}

题目2:删除链表的倒数第N个节点


给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

思路:先遍历一遍链表得到链表长度len,之后将指针point移到第len-n个节点处,也就是要删除节点的前一个节点,最后完成删除。但要注意一种特殊情况,就是要删除节点为链表第一个节点这种情况。

ListNode* removeNthFromEnd(ListNode* head, int n) 
{
         ListNode* point = head;
         int len = 0;
	 while(point!=NULL)
	 {
	 	len += 1;
	 	point = point->next;
	 }
	 point = head;
	 if(n==len)
	 {
	 	head = head->next;
	 }
	 else
	 { 
	 	int index = len-n;
		for(int i=0;i<index-1;i++)
		    point = point->next;
		point->next = point->next->next;    
	 } 	    
	 return head;
}

题目3:反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

思路:利用头插法翻转链表,用point指针依次遍历链表元素,每遍历一个元素就将其放在结果链表的头结点之前,再用头结点指针指向该元素,重复上述过程,直到point指向NULL。

ListNode* reverseList(ListNode* head) 
{
	if(head==NULL||head->next==NULL)
	    return head;
	ListNode* point = head->next;
	head->next = NULL;
	while(point!=NULL)
	{
	    ListNode* temp = point->next;
	    point->next = head;
	    head = point;	
	    point = temp;		
	}
        return head;    
}

题目4:合并两个有序链表

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

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

思路:使用双指针分别指向两个链表的第一个节点,比较两指针指向节点的值大小,将较大节点放入结果链表,之后将较大节点指针后移,重复上述过程,直到有一个指针指向NULL,最后将还有剩余节点的链表的节点全部放入结果链表。

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) 
{
	ListNode* head = new ListNode(0);
	ListNode* point = head;
	ListNode* point1 = l1;
	ListNode* point2 = l2;
        while(point1!=NULL&&point2!=NULL)
	{
		int num1 = point1->val;
    	        int num2 = point2->val;
		if(num1<=num2)
		{
			point->next = point1;
			point1 = point1->next; 
		}
		else
		{
			point->next = point2;
			point2 = point2->next;
		}
		point = point->next;
         }    
         if(point1!=NULL)
            point->next = point1;
         if(point2!=NULL)
            point->next = point2;
         return head->next;
}

题目5:回文链表

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

思路1:利用快慢指针,慢指针走一步,快指针走两步,当快指针走到末尾,慢指针正好在链表中节点(链表长度为奇则正好在中点,链表长度为偶则在上中点处),在慢指针移动过程中将前半部分链表节点记录在一个栈中,在慢指针到达中点后再继续向后移动慢指针遍历后半链表,并将慢指针指向节点与栈顶元素比较,若相等则栈顶元素出栈,继续遍历,若不等则返回false,重复上述过程,直到链表全部遍历完成。

bool isPalindrome(ListNode* head) 
{
	if(head==NULL||head->next==NULL)
	    return true;    
	vector<ListNode*> stack;
	//快慢指针找中点 
	ListNode* slow = head;
	ListNode* fast = head;
	while(fast->next!=NULL&&fast->next->next!=NULL)
	{		
		stack.push_back(slow);
		slow = slow->next;
		fast = fast->next->next;
	}
	//当慢指针到达中点时已经跳出循环,中点并未入栈,链表长度为奇时,
	//中节点不参与回文判断,所以只处理链表长度为偶的情况即可。 	 
	if(fast->next!=NULL&&fast->next->next==NULL)
	    stack.push_back(slow);
	//遍历后半链表,并判断是否回文    
	slow = slow->next;    
	while(slow!=NULL)
	{
		if(slow->val!=stack.back()->val)
		    return false;
		stack.pop_back();
		slow = slow->next;	    
	}    
        return true;    
}

思路2:同样利用快慢指针,找到链表的中节点;之后翻转后半部分链表,再利用双指针,一个指向head,一个指向后半部分第一个节点,比较两指针指向节点的值,若相等则两指针同时向后移动,若不等则返回false;重复上述过程直到后半部分指针指向空。

bool isPalindrome(ListNode* head) 
{
	if(head==NULL||head->next==NULL)
	    return true;    
	//快慢指针找中点 
	ListNode* slow = head;
	ListNode* fast = head;
	while(fast->next!=NULL&&fast->next->next!=NULL)
	{		
		slow = slow->next;
		fast = fast->next->next;
	}
	//翻转后半部分链表 
	ListNode* point = slow->next->next;
	slow->next->next = NULL;
	while(point!=NULL)
	{
		ListNode* temp = point->next;
		point->next = slow->next;
		slow->next = point;
		point = temp; 
	}
	//判断是否回文 
	fast = slow->next;
	slow = head;
	while(fast!=NULL)
	{
		if(slow->val!=fast->val)
		    return false;
		fast = fast->next;
		slow = slow->next;    
	}
	return true;
}

题目6:环形链表

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

思路:快慢指针的经典应用,让快慢指针从链表头开始遍历,快指针向前移动两个位置,慢指针向前移动一个位置,如果快指针到达NULL,说明链表以NULL为结尾,不是循环链表。反之快指针追上慢指针,则表示出现了循环。

bool hasCycle(ListNode *head) 
{
	if(head==NULL)
            return false;
	ListNode* slow = head;
	ListNode* fast = head;
	while(fast!=NULL&&fast->next!=NULL)
	{
	    slow = slow->next;
	    fast = fast->next->next;
	    if(slow==fast)
                return true;
	}
	return false;            
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值