数据结构经典习题【part2】

一些数据结构经典习题
part 2
链表

1.给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* prev=NULL;
	struct ListNode* cur=head;
	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;
}

在这里插入图片描述
此题是对链表删除功能的一个应用,要考虑到特殊情况头删。

做法二

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
struct ListNode
{
	int val;
	struct ListNode* next;
};

struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* tail = NULL;
	struct ListNode* cur = head;

	//哨兵位
	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;
}

int main()
{
	struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n1);

	struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n2);

	struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n3);

	struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode));
	assert(n4);

	n1->val = 7;
	n2->val = 7;
	n3->val = 8;
	n4->val = 7;

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;

	struct ListNode* head = removeElements(n1, 7);

	return 0;
}

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表

方法一:头插法

struct ListNode* reverseList(struct ListNode* head)
{
	struct ListNode* newhead=NULL;
	struct ListNode* cur=head;
	while(cur)
	{
		struct ListNode* next=cur->next;
		
		cur->next=newhead;
		newhead=cur;
		
		cur=next;
	}
	return newhead;
}

在这里插入图片描述

方法二:改变指向

struct ListNode* reverseList(struct ListNode* head)
{
	if(head==NULL)//防止n2->next出现野指针
		return NULL;
	struct ListNode *n1,*n2,*n3;
	n1=NULL;
	n2=head;
	n3=n2->next;
	while(n2)
	{
		//改指向
		n2->next=n1;
		//迭代,改值
		n1=n2;
		n2=n3;
		//判断是否结束
		if(n3)
		n3=n3->next;
	}
	return n1;
}

链表的中间的结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

struct ListNode* middleNode(struct ListNode* head)
{
	struct ListNode * slow,*fast;//*不要掉了
	fast=slow=head;//顺序不能变
	while(fast&&fast->next)
	{
		slow=slow->next;
		fast=fast->next->next;//一次走两格
	}
	return slow;
}

分两种情况,奇数个和偶数个
在这里插入图片描述

链表中倒数第k个结点

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

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k )
{
	struct ListNode *fast,*slow;
	slow=fast=pListHead;
	//fast提前走k步
	while(k--)
	{
		//防止还没走到k步,链表就结束了
		if(fast==NULL)
			return NULL;
		fast=fast->next;
	}
	while(fast)
	{
		fast=fast->next;
		slow=slow->next;
	}
  return slow;
}

此题使用双指针,快慢指针,注意链表长度和k的关系
在这里插入图片描述

合并两个有序链表

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

在这里插入图片描述

解法一:
不带哨兵位头结点

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
	if(list1==NULL)
		return list2;
	if(list2==NULL)
		return list1;
		
	struct ListNode* head,*tail;
	head=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)
		tail->next=list1;
	if(list2)
		tail->next=list2;
	
	return head;
}

在这里插入图片描述
首先判断两个链表是否为空,为空返回另外一个链表
不为空则进入循环,从头判断链表的值大小,其中要判断尾指针是否为空,为空相当于头插链表。
最后,当一个链表为空,直接将另一个链表剩余值返回到尾指针。

带哨兵位头结点解法

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
	struct ListNode* head,*tail;
	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)
		tail->next=list1;
	if(list2)
		tail->next=list2;
		
	struct ListNode* list=head->next;
	free(head);
	return list;
}

链表分割

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

此题使用了哨兵位头结点

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x)
    {
    	struct ListNode* lesshead,*lesstail,*greaterhead,*greatertail;
    	lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
    	greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
    	lesstail->next=NULL;
    	greatertail->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;
    	}
    	lesstail->next=greaterhead->next;
    	greatertail->next=NULL;
    	
    	struct ListNode* list=lesshead->next;
    	free(lesshead);
    	free(greaterhead);
	return list;
	}
}
    			

在这里插入图片描述

链表的回文结构

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

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

在这里插入图片描述

struct ListNode* middleNode(ListNode* head)
{
	struct ListNode * fast,*slow;
	slow=fast=head;
	while(fast&&fast->next)
	{
		slow=slow->next;
		fast=fast->next->next;
	}
	return slow;
}
struct ListNode* reverseList(ListNode* head)
{
	if(head==NULL)
		return NULL;
	struct ListNode *n1,*n2,*n3;
	n1=NULL:
	n2=head;
	n3=n2->next;
	while(n2)
	{
		n2->next=n1;
		n1=n2;
		n2=n3;
		if(n3)
		n3=n3->next;
	}
	return n1;
}
	
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A)
    {
    	struct ListNode* mid=middleNode(A);
    	struct ListNode* rhead=reverseList(mid);
    	while(A&&rhead)
    	{
    		if(a->val!=rhead->val)
    			return false;
    		else
    		{
    			A=A->next;
    			rhead=rhead->next;
    		}
    	}
    	return true;
    }
 };

此题思路是:先找到中间结点,再将后半部分反转,前部分和后半部分进行比较,相同返回true,不同返回false.

相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
在这里插入图片描述

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
	struct ListNode* curA=headA,*curB=headB;
	int lenA=1,lenB=1;
	while(curA->next)
	{
		curA=curA->next;
		lenA++;
	}
	while(curB->next)
	{
		curB=curB->next;
		lenB++;
	}
	if(curA!=curB)
	{
		return NULL;
	}
	else
	{
		struct ListNode* longlist=headA,*shortlist=headB;
		if(lenA<lenB)
		{
			longlist=headB;
			shortlist=headA;
		}
		int gap=abs(lenA-lenB);
		while(gap--)
		{
			longlist=longlist->next;
		}
		while(shortlist!=longlist)
		{
			shortlist=shortlist->next;
			longlist=longlist->next;
		}
		return shortlist;
	}
}		

基本思路是:先求出两个链表的长度,同时如果两个链表不相等则说明不相交,返回NULL。让长的链表先走差距步,当长链表等于断链表时,返回短链表此时就是相交结点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值