C语言数据结构之单链表部分OJ题

(只有主要思路,不是具体代码)

第一题:翻转链表

	SLTNode* newphead = NULL;
	SLTNode* cur = *pphead;
	SLTNode* Next = (*pphead)->next;
 	while (cur)
	{
		cur->next = newphead;
		newphead = cur;
		cur = Next;
		if(Next)
		  Next = Next->next;
	}
	*pphead = newphead;
	return *pphead;
}

 

第二题:求链表中间元素

	//方法一:通过计算链表中元素个数/2来确定中间元素
	//此时时间复杂度为f(n)
	/*SLTNode* tail = phead;
	int i = 0;
	while (tail)
	{
		tail = tail->next;
		i++;
	}
	int n = i / 2;
	SLTNode* find = phead;
	while (n)
	{
		find = find->next;
		n--;
	}
	return find;*/
	//方法二:快慢指针
	SLTNode* slow, * fast;
	slow = phead;
	fast = phead;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}

情况二:

第三题:寻找倒数第K个数

先让fast走K步,然后再slow,fast依次前进 

	//寻找倒数第K个数
	//方法一:先求出链表中元素个数,然后求出正数第几个元素
	//方法二:快慢指针
	if (pos > 0)
	{
		SLTNode* slow, * fast;
		slow = phead;
		fast = phead;
		for (; pos > 0; pos--)
		{
			if (fast == NULL)
				return NULL;
			fast = fast->next;
		}
		while (fast)
		{
			slow = slow->next;
			fast = fast->next;
		}
		return slow;
	}
	else
	{
		return -1;
	}

第四题:将两个升序链表合并成一个新的升序链表

    SLTNode* tail = NULL;
    SLTNode* l1 = phead1;
    SLTNode* l2 = phead2;
    SLTNode* phead3 = NULL;
    if (l1 == NULL)
    {
        return l2;
    }
    if (l2 == NULL)
    {
        return l1;
    }
    //不带哨兵位的方法:其实就是在两个链表当中选择一个链表的头为新链表的头
    /*while (l1 && l2)
    {
        if (l1->data < l2->data)
        {
            if (phead3 == NULL)
            {
                phead3 = tail = l1;
            }
            else
            {
                tail->next = l1;
                tail=l1;
            }
            l1 = l1->next;
        }
        else
        {
            if (phead3 == NULL)
            {
                phead3 = tail = l2;
            }
            else
            {
                tail->next = l2;
                tail = l2;
            }
            l2 = l2->next;
        }

        
    }
    if (l1)
    {
        tail->next = l1;
    }
    if (l2)
    {
        tail->next = l2;
    }
    return phead3;*/
    //带哨兵位的方法:其实就是开辟一个空间作为新链表的头
    SLTNode* head=tail=(SLTNode*)malloc(sizeof(SLTNode));
    while (l1 && l2)
    {
        if (l1->data < l2->data)
        {
            tail->next = l1;
            tail = l1;
            l1 = l1->next;
        }
        else
        {
            tail->next = l2;
            tail = l2;
            l2 = l2->next;
        }
    }
    if(l1)
    {
        tail->next = l1;
    }
    if (l2)
    {
        tail->next = l2;
    }
    SLTNode* list = head->next;
    free(head);
    return list;
}

第五题:分割链表

重新开辟两个链表,一个放大于K的数,一个放小于K的数,在将两个链表链接起来。

SLTNode* cur = pphead;
SLTNode* phead1 , * tail1;
SLTNode* phead2 , * tail2;
phead1 = tail1 = (SLTNode*)malloc(sizeof(SLTNode));
phead2 = tail2 = (SLTNode*)malloc(sizeof(SLTNode));
while (cur)
{
	if (cur->data < x)
	{
		tail1->next= cur;
		tail1 = cur;
	}
	else
	{
		tail2->next = cur;
		tail2 = cur;
	}
	cur = cur->next;
}
tail1->next = phead2->next;
tail2->next = NULL;
SLTNode* newhead = phead1->next;
free(phead1);
free(phead2);
return newhead;

第六题:比如一个链表:1->2->2->1;或者1->2->3->2->1
              链表的回文


	//快慢指针寻找中间的节点
	SLTNode* mid = SListFindMiddle(plist);
	//旋转中间节点后的节点
	SLTNode* nhead = SListRevereseLisst(&mid);
	SLTNode* cur1 = plist;
	SLTNode* cur2 = nhead;

	while (cur1 && cur2)
	{
		if (cur1->data == cur2->data)
		{
			cur1 = cur1->next;
			cur2 = cur2->next;
		}
		else
		{
			return 0;
		}
	}
	return 1;

第七题:相交链表

先计算两个链表中节点的个数,然后让长链表先走节点个数的差数,然后两个链表同时走
因为当两个链表个数相同时方便判断,所以要想办法将两个链表变为长度相同的链表。

int lenA = 1;
int lenB = 1;
SLTNode* tailA = plist1;
SLTNode* tailB = plist2;
while (tailA->next)
{
	lenA++;
	tailA = tailA->next;
}
while (tailB->next)
{
	lenB++;
	tailB = tailB->next;
}
if (tailA != tailB)
{
	return NULL;
}
//abs是取绝对值
int gap = abs(lenA - lenB);
SLTNode* longplist = plist1;
SLTNode* smalllist = plist2;
if (lenA < lenB)
{
	longplist = plist2;
	smalllist = plist1;
}
while (gap--)
{
	longplist = longplist->next;
}
while (longplist != smalllist)
{
	longplist = longplist->next;
	smalllist = smalllist->next;
}
return longplist;

第八题:循环链表

判断一个链表是否为循环链表
用快慢指针来判断,本质上为追及问题
 fast先进循环,slow后进循环,再循环中fast迟早追上slow

环形链表延伸问题:
1:为什么slow与fast一定会在环中相遇?会不会在环里错过永远遇不上?
结论:他们一定会相遇
解释:当slow一次走一步,fast一次走两步,当slow走到环形链表的入口时,二者开始追及,此时二者之间的距离一次减小1个
那么必定会追上
2:为什么fast一次走两步,当fast一次走n>2步行不行?
 结论:不行。
 解释:比如fast一次走3步:当slow走到环形链表的入口时,二者开始追及,此时二者之间的距离一次减小2个,如果当slow刚进入环形链表的入口
时,若二者之间的距离为奇数,那么在这次追及的过程中不会相遇,此时fast在前,slow在后,二者之间差1,那么此时fast又开始追及slow,此时
二者之间的距离为N(环形链表的长度)-1,若N-1还为奇数则永远追不上,若N-1为偶数,则可以追上。

	SLTNode* slow = plist;
	SLTNode* fast = plist;
	//考虑奇数与偶数的问题
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next ;
		if (slow == fast)
		{
			//相遇
			SLTNode* meet = slow;
			SLTNode* begin = plist;
			//公式证明,一个从链表的头开始走,一个从相遇点开始走,两者相遇时必为入口点
			//从头到入口点的距离为L,入口点到相遇点的距离为X,
			//相遇时slow走的距离为L+X;fast走的距离为L+X+n*C(环形链表的距离),n>=1
			//2(L+X)=L+X+n*C
			//L=n*C-X  ->  L=(n-1)*C+C-X;
			//

			while (meet != begin)
			{
				meet = meet->next;
				begin = begin->next;
			}
			//寻找入口点
			return meet;
			//判断是否是循环链表
			//return 1;
		}
	}
	return 0;

第九题:复制带随机指针的链表

	//1,先复制一个节点插在两个节点中间
    struct Node*cur=head;
    while(cur)
    {
        struct Node*newNode=(struct Node*)malloc(sizeof(struct Node));
        newNode->val=cur->val;
        newNode->next=cur->next;
        cur->next=newNode;
        cur=newNode->next;
    }
    cur=head;
  
    //2,复制节点的random其实就已经等于对应节点random的next
    while(cur)
    {
        struct Node*newhead=cur->next;
        //开始向复制节点里复制random
        if(cur->random==NULL)
        {
            newhead->random=NULL;
        }
        else
        {
            newhead->random=cur->random->next;
        }
        cur=newhead->next;
    }
    //3,开始将复制链表提出来
    cur=head;
    struct Node*Head=NULL,*tail=NULL;
    while(cur)
    {
        //用来记录复制链表的下一个节点
         struct Node*copy=cur->next;
         //用来记录原链表下一个节点
         struct Node*next=copy->next;  
        //连接复制链表的
        if(Head==NULL)
        {
            Head=tail=cur->next;
        }
        else
        {
            tail->next=copy;
            tail=copy;
        }
        cur->next=next;
        cur=next;
    }
return Head;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值