单链表的几道简单练习题

<1.简单:

             移除链表元素(来源力扣)

            合并两个有序链表(来源力扣)

            反转链表(来源力扣)

            链表的中间结点(来源力扣)

            链表中倒数第k个结点(来源牛客)OJ链接:链表中倒数第k个结点_牛客题霸_牛客网

<2.较难:

           链表分割(来源牛客)OJ链接:链表分割_牛客题霸_牛客网

           链表的回文结构 (来源牛客)OJ链接:链表的回文结构_牛客题霸_牛客网

///

1移除链表元素:

 解题思路:创建哨兵位,把不是val的节点拿来尾插在哨兵位后面,以此类推,是val的先记录下一个节点的地址,再释放该节点;具体代码如下:

struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* newhead = head;//创建临时变量,用来遍历链表
	struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));//加上哨兵位;
	if (guard == NULL)//判断是否开辟空间成功;
	{
		perror("malloc");
		exit(-1);
	}
	guard->next = NULL;
	struct ListNode* ptali = guard;创临时节点,向后走;
	if (head == NULL)//如果是空链表,直接返回NULL;
	{
		return NULL;
	}
	while (newhead != NULL)//遍历链表;
	{
		if (newhead->val!= val)//不是val,把链表的节点赋给带哨兵位的新的链表
		{
			ptali->next = newhead;
			ptali = newhead;//新链表往后走;
			newhead = newhead->next;//原来的链表也往后走;
		}
		else//是val,保留原来链表节点的下一个节点,释放现在的节点,将下一个节点赋予newnode,实现向后走;
		{
			struct ListNode* next = newhead->next;
			free(newhead);
			newhead= next;
		}
	}
	if (newhead== NULL)//当原来的链表遍历完;
	{
		ptali->next = NULL;//把新链表的尾节点的next置为空;
	}
	 struct ListNode*newnode=guard->next;//创建临时节点,记录下要返回的节点;
     free(guard);//释放开辟的哨兵位;
     return newnode;返回新链表的头节点;
}

//////

2合并两个有序链表:

 解题思路:创建一个哨兵位,依次比较尾插,具体代码如下:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    //创建一个哨兵位;
    struct ListNode*phead=(struct ListNode*)malloc(sizeof(struct ListNode));
    phead->next=NULL;
    struct ListNode*head=phead;//创建临时节点,方便遍历数组;
    if(list1==NULL&&list2==NULL)//如果两个链表都为空,直接返回NULL;
    {
        return NULL;
    }
    while(list1!=NULL&&list2!=NULL)//如果都不为NULL,开始遍历比较;
    {
        if(list1->val<=list2->val)
        {
            head->next=list1;
            head=head->next;
            list1=list1->next;
        }
        else
        {
            head->next=list2;
            head=head->next;
            list2=list2->next;
        }
    }
//如果出现上面的遍历结束后,还有一个链表还有元素,那么不用再比较,直接尾插
    while(list1!=NULL)//如果第一个链表还有元素;
    {
        head->next=list1;
        head=head->next;
        list1=list1->next;
    }
    while(list2!=NULL)//如果第二个链表还有元素;
    {
        head->next=list2;
        head=head->next;
        list2=list2->next;
    }
    struct ListNode*pphead=phead->next;//创建临时节点,记录要返回的头节点;
    free(phead);//释放哨兵位;
    return pphead;//返回头节点;
}

//////

3反转链表:

 解题思路:创建一个空指针的节点,然后遍历原链表依次头插;具体代码如下:

struct ListNode* reverseList(struct ListNode* head)
{
    if(head==NULL)//如果原链表为空,直接返回空;
    {
        return NULL;
    }
    struct ListNode*phead=NULL;//创建临时空节点,
    while(head!=NULL)//遍历原链表,头插;
    {
        struct ListNode*next=head->next;//先把原链表节点的下一个节点记录下来;
        head->next=phead;//头插;
        phead=head;//让之前的后一个节点成为头节点;
        head=next;//将记录下的下一个节点赋给原链表节点,实现遍历;
    }
    return phead;//返回反转后的头结点
}

///

///

4链表的中间节点:

 解题思路:利用快慢指针slow,fast,当快指针fast走到尾节点或者NULL,慢指针slow就在中间节点;具体代码如下:

struct ListNode* middleNode(struct ListNode* head)
{
    //创建快慢指针,一开始都在头节点;
    struct ListNode*slow=head;//慢指针;
    struct ListNode*fast=head;//快指针;
    while(fast!=NULL&&fast->next!=NULL)//如果是偶数个元素,走到尾节点为NULL就结束,如果是奇数个元素,走到尾节点下一个节点NULL结束;
    {
        slow=slow->next;//慢指针一次走一步;
        fast=fast->next->next;//快指针一次走慢指针的两倍步长;
    }
    return slow;//返回慢指针,慢指针所指向的节点就是中间节点;
}

//////

5链表中倒数第k个节点:

 

解题思路:创建快慢指针,让快指针走差异步(k步),然后再一起走,当快指针走到尾节点时,慢指针就是链表中倒数第k个节点;具体代码如下:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    //创建快慢指针;
    struct ListNode*slow=pListHead;//慢指针;
    struct ListNode*fast=pListHead;//快指针;
    if(pListHead==NULL)//如果链表为空,直接返回NULL;
    {
        return NULL;
    }
    while(k--)//先让快指针走差异步;
    {
        if(fast==NULL)//如果k大于链表节点的个数,直接返回NULL;
        {
            return NULL;
        }
        fast=fast->next;
    }
    while(fast!=NULL)//遍历链表;
    {
        slow=slow->next;
        fast=fast->next;
    }
    return slow;//返回慢指针,此时慢指针就是倒数第k个节点;

//////

1链表分割:

 解题思路:创建两个哨兵位,一个存大于x的节点,一个存小于x的节点,最后,将两个带哨兵位的新链表链接在一起,释放掉哨兵位;具体代码如下:

class Partition {
public:
   ListNode * partition(ListNode * pHead, int x)
    {
       //创建两个哨兵位;
	   ListNode*pheadA = (ListNode*)malloc(sizeof(ListNode));
	   ListNode*pheadB = (ListNode*)malloc(sizeof(ListNode));
       //再创建两个临时节点,方便向后链接;
	   ListNode*ptali = pheadA;
	   ListNode*plist = pheadB;
	   ListNode*cur = pHead;//创建临时节点,遍历链表;
	   ptali->next= NULL;
	   plist->next =NULL;
	   while (cur != NULL)
	     {
		    if (cur->val < x)//小于x的存在pheadA链表中;
		    {
			 ptali->next = cur;
			 ptali = ptali->next;
		    }
		   else大于x的存在pheadB链表中;
		   {
			 plist->next = cur;
			 plist = plist->next;
		   }
		     cur = cur->next;//向后遍历;
        }
	    ptali->next = pheadB->next;//创建临时变量,把大于x的链表的头节点记录下来;
	    plist->next = NULL;//将小于x的链表尾节点置为NULL,防止指向之前的节点形成闭环;
	    pHead = pheadA->next;
        //释放哨兵位
	    free(pheadA);
	    free(pheadB);
	    return pHead;

    }
};

//////

2链表的回文结构:

  解题思路:先用快慢指针找到中间节点,然后反转链表,最后遍历比较反转后的后半边链表和整个链表;具体代码如下:

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*swaprome(struct ListNode* head)
{
     struct ListNode*cur=head;
     struct ListNode*pphead=NULL;
    while(cur!=NULL)
    {
     struct ListNode*next=cur->next;
        cur->next=pphead;
        pphead=cur;
        cur=next;
    }
    return pphead;
}
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) 
    {
        struct ListNode*phead=middleNode(A);//调用寻找中间节点的函数;
        struct ListNode*ptali=swaprome(phead);//调用反转链表的函数;
        while((A!=NULL)&&(ptali!=NULL))//比较反转后的半边链表和整个链表;
        {
            if(A->val!=ptali->val)
            {
                return false;//如果有不同,直接返回false;
            }
            //实现向后遍历
            A=A->next;
            ptali=ptali->next;
        }
        return true;//如果反转后的半边链表遍历完没有不同的,说明此链表为回文结构,返回true;
    }
    
};

至此,这是我对这几道简单的单链表题目的理解与分析,如果有错误或疏漏的地方,请各位大佬不吝赐教,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值