数据结构——常见链表算法题

本文详细介绍了8种常见的链表算法题,包括反转链表、移除重复节点、查找中间节点、找到倒数第k个节点、合并升序链表、判断环形链表、寻找两链表交点以及判断回文链表。每个问题都提供了相应的C++实现和思路解析,适合数据结构和算法学习者参考。

数据结构——常见链表算法题(C++运行)

本文介绍常见的有关链表的算法题,值得一提的是,该代码无需在力扣上,可在本地运行成功。
题目八道:
1.反转链表
2.移除链表中的重复节点
3.查找链表中的中间节点
4.找到链表中的倒数第k个节点
5.合并两个升序链表,并同样按照升序排列
6.判断是否为环形链表
7.查找两链表的交点,相交链表
8.判断是否为回文链表(空链表和单节点链表也算)

创建链表类和方法类以及打印链表方法

#include <iostream>
using namespace std;

//定义链表结构
class ListNode{
public:
    int val;
    ListNode* next;
    //结构体构造函数
    ListNode(int x);
    ListNode* listNode_create();

//默认访问权限为private   
};

ListNode::ListNode(int x=0):val(x),next(NULL)
{
}//列表初始化用法

ListNode* ListNode::listNode_create(){
    cout<<"请输入链表长度:";
    int num =0;
    cin>>num;
    int listValue=0;
    ListNode * head ;
    ListNode * tempNode;
    ListNode * node = head;
    head = NULL;
    tempNode = NULL;

    for (int i =0;i<num;i++){
        if(head == NULL){
            head = this;
            node = head;
        }else{
            node = new ListNode;
            cout<<"输入链表节点的值node->val:";
            cin>>node->val;
            tempNode->next = node;
        }
        tempNode = node;
        tempNode->next = NULL;    
    }
    cout<<"链表完成创建"<<endl;
    return head;
} 

class Solution{
    public:
        Solution();
        ListNode* reverseNode(ListNode* head);//反转链表
        ListNode* removeSameNode(ListNode* head);//移除重复链表
        ListNode* findLastKnode(ListNode* head,int k);//找出倒数第K个链表节点
        ListNode* midNode(ListNode* head);//找出链表中间节点,注意偶数时返回第一个节点和第二个节点的循环结束区别
        ListNode* mergeListNode(ListNode* list1,ListNode* list2);//两个升序链表合并成一个升序链表
        ListNode* circleListNode(ListNode* head);//找出环形列表的节点
        ListNode* findcrossingListNode1(ListNode* list1,ListNode* list2);//查找相交节点,方法一
        ListNode* findcrossingListNode2(ListNode* list1,ListNode* list2);//查找相交节点,方法二
        bool findPalindrome(ListNode* head);//判断是否为回文列表
};

Solution::Solution(){}

//打印链表
void printList(ListNode* head){
    cout<<"链表:head";
    ListNode* temp = head;
    while(temp != NULL){
        cout<<"<<"<<temp->val;
        temp = temp->next;
    }
}

//打印链表,k最大打印个数
void printList(ListNode* head,int k){
    cout<<"链表:head";
    ListNode* temp = head;
    while(temp != NULL && k>0){
        cout<<"<<"<<temp->val;
        temp = temp->next;
        k--;

    }

}

1.反转链表

思路:定义三个链表:former:存储完成反转的前一个链表,mid:正在处理的节点,latter:下一个待处理的节点。

方法:

ListNode* Solution::reverseNode(ListNode* head){
    if(head==NULL || head->next == NULL){
        return head;
    }

    ListNode* former = NULL;//保存前一个链表节点
    ListNode* mid = head;//正在处理的中间节点
    ListNode* latter = NULL;//保存下一个需要处理的节点
    while(mid != NULL){
        latter = mid->next;//保存下一个节点地址
        mid->next = former;//反转当前节点,next指向上一个链表节点
        former = mid;//former储存节点
        mid = latter;//处理下一个节点
    }
    return former;//返回节点
}

main函数:

int main(){
    
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(1);
    ListNode* node3 = new ListNode(5);
    ListNode* node4 = new ListNode(3);
    ListNode* node5 = new ListNode(1);
    ListNode* node6 = new ListNode(1);
   
    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
	
    Solution* s = new Solution();
    //反转链表
    cout<<"原链表:";
    printList(head);
    cout<<endl<<"反转后列表:";
    head = s->reverseNode(head);
    printList(head);
    return 0;
}

在这里插入图片描述

2.移除链表中的重复节点

使用数组,第一次出现的对应数组值置,随后再遇到数组值为1的节点将其移除。“空间换时间”

方法:

ListNode* Solution::removeSameNode(ListNode* head){
    if(head == NULL||head->next == NULL){
        return head;
    }
    ListNode* cur = head;//正在识别的listnode
    ListNode* pre = head;//已经处理好的listNode
    int numIndex[10001] = {0};
    //numIndex[pre->val] = 1;
    while(cur != NULL){
        if(numIndex[cur->val] == 0){
            numIndex[cur->val] = 1;
            pre = cur;
            cur = cur->next;
            
        }else{
            pre->next = cur->next;
            cur = cur->next;
        }
    }
    return head; 
}

main函数:

int main(){
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(1);
    ListNode* node3 = new ListNode(5);
    ListNode* node4 = new ListNode(3);
    ListNode* node5 = new ListNode(1);
    ListNode* node6 = new ListNode(1);
  
    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
    Solution* s = new Solution();
    cout<<"原链表:";
    printList(head);
    cout<<endl<<"移除重复节点后:";
    s->removeSameNode(head);
    printList(head);
    return 0;
}

在这里插入图片描述

3.查找链表中的中间节点

快慢指针,快指针走两步,慢指针走一步,最终快指针结束循环,慢指针到达中点。
注意:此处规定:为偶数,返回第二个节点,则判断条件为fast !=NULL && fast->next !=NULL;
若想要返回第一个节点则:fast->next != NULL && fast->next->next != NULL.

方法:

//找出链表中间节点,注意偶数时返回第一个节点和第二个节点的循环结束区别
//返回第二个节点:while(fast != NULL && fast->next !=NULL)
//返回第一个节点:while(fast->next != NULL && fast->next->next !=NULL)
ListNode* Solution::midNode(ListNode* head){
    ListNode* slow = head;
    ListNode* fast = head;
    if(head == NULL && head->next == NULL){
        return head;
    }
    while(fast != NULL && fast->next !=NULL){
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

main函数:

int main(){
    
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(1);
    ListNode* node3 = new ListNode(5);
    ListNode* node4 = new ListNode(3);
    ListNode* node5 = new ListNode(1);
    ListNode* node6 = new ListNode(1);
   
    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
   
    Solution* s = new Solution();
    cout<<"原链表:";
    printList(head);

	cout<<endl<<"返回链表的中间节点:";
    ListNode* mid = s->midNode(head);
    printList(mid);
	return 0;

在这里插入图片描述

4.找到链表中的倒数第k个节点

双指针,一个指针比另一个指针快走k个节点,速度相同,最终快走的节点到头后,另一个指针所在节点为倒数第k个节点。

方法:

ListNode* Solution::findLastKnode(ListNode* head,int k){//快慢指针
    ListNode* slow = head;
    ListNode* fast = head;
    int i =0;
    for(i=0;i<k;i++){
        fast=fast->next;
    }

    while(fast!=NULL){
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

main函数:

int main(){
    
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(1);
    ListNode* node3 = new ListNode(5);
    ListNode* node4 = new ListNode(3);
    ListNode* node5 = new ListNode(1);
    ListNode* node6 = new ListNode(1);
    
    
    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
    //node6->next = node3;

    Solution* s = new Solution();
    cout<<"原链表:";
    printList(head);
    cout<<endl<<"返回倒数第k个链表节点:";
    ListNode* temp = s->findLastKnode(head,3);
    printList(temp);
    return 0;
}

在这里插入图片描述

5.合并两个升序链表,并同样按照升序排列

重新定义一个链表,两个链表依次比较,较小值拼接到新建链表上,若有一链表全部拼接完,直接将另一个链表(已升序排列)的剩余依次拼接到新建链表上。

方法:

ListNode* Solution::mergeListNode(ListNode* list1,ListNode* list2){
    if(list1 == NULL){
        return list2;
    }
    if(list2 == NULL){
        return list1;
    }
    ListNode* head = new ListNode();
    ListNode* cur = head;
    while(list1 != NULL && list2 != NULL){
        if(list1->val < list2->val){
            cur->next = list1;
            list1=list1->next;
        }else{
            cur->next = list2;
            list2=list2->next;
        }
        cur = cur->next;
    }
    if(list1 ==NULL){
        cur->next = list2;
    }else{
        cur->next = list1;
    }
    return head->next;//返回head的后一个链表节点
}

main函数:

int main(){ 
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(4);
    ListNode* node5 = new ListNode(5);
    ListNode* node6 = new ListNode(6); 
    head->next = node2;
    node2->next = node3;
    node3->next = node4;            
    Solution* s = new Solution();
    ListNode* head2 = new ListNode(0);
    head2->next = node5;
    node5->next = node6;
    
    cout<<"原链表list1:";
    printList(head);
    cout<<"原链表list2:";
    printList(head2);

	cout<<endl<<"返回合并后的链表:";
    ListNode* mergeNode = s->mergeListNode(head,head2);
    printList(mergeNode);
	return 0}

6.判断是否为环形链表

在这里插入图片描述
快慢指针思路

方法:

//找出环形链表的节点,快慢指针,有环则返回环节点,无环则返回NULL
ListNode* Solution::circleListNode(ListNode* head){
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast !=NULL && fast->next != NULL){
        fast=fast->next->next;
        slow=slow->next;
        if(slow == fast){
            fast = head;
            while(fast != slow){
                fast = fast->next;
                slow = slow->next;
            }
            return slow;
        }
    }
    return NULL;
}

main函数:

int main(){
    
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(4);
    ListNode* node5 = new ListNode(5);
    ListNode* node6 = new ListNode(6);
   
    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
    node6->next = node3;
	Solution* s = new Solution();
    cout<<"原链表list1:";
    printList(head,10);
	cout<<endl<<"返回环形后的链表:";
    ListNode* circlehead = s->circleListNode(head);
    printList(circlehead,10);
    return 0;
}

7.查找两链表的交点,相交链表

//查找相交节点,方法一,a+c+b=d+c+b;最后相较于交点
 ListNode* Solution::findcrossingListNode1(ListNode* list1,ListNode* list2){
    ListNode* p=list1;
    ListNode* q=list2;
    while(p !=q){
        if(p==NULL){
            p = list2;
        }else{
            p = p->next;
        }
        if(q == NULL){
            q = list1;
        }else{
            q = q->next;
        }
    }
    return q;
}

//查找相交节点,方法二,迭代法
ListNode* Solution::findcrossingListNode2(ListNode* list1,ListNode* list2){
    ListNode* p=list1;
    ListNode* q=list2;
    while(p !=NULL){
        q=list2;
        while(q !=NULL){
            if(p == q){
                return p;
            }
            q=q->next;
        }
        p = p->next;
    }
    return p;
}

main函数:

int main(){
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(4);
    ListNode* node5 = new ListNode(5);
    ListNode* node6 = new ListNode(6);
    
    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
    Solution* s = new Solution();
    ListNode* head2 = new ListNode(0);
    head2->next = node3;
    cout<<"原链表list1:";
    printList(head);
    cout<<"原链表list2:";
    printList(head2);

	cout<<endl<<"返回交点节点:";
    ListNode* crossingNode = s->findcrossingListNode1(head,head2);//方法一
    //ListNode* crossingNode = s->findcrossingListNode2(head,head2);//方法二
    printList(crossingNode);
    return 0;
}

在这里插入图片描述

8.判断是否为回文链表(空链表和单节点链表也算)

提取中间节点,反转链表,再比较是否相等

//判断是否为回文列表:先取中间节点(如为偶数取第二个),然后链表反转,最后判断前面链表是否与后面链表相同
bool Solution::findPalindrome(ListNode* head){
    ListNode* list1 = head;
    ListNode* list2 = head;
    //if判断语句可有可无,不影响结果输出
    if(head==NULL || head->next == NULL){
        return true;
    }
    
    //去链表中间节点
    while(list1 != NULL && list1->next != NULL){
        list1 = list1->next->next;
        list2 = list2->next;
    }

    //反转链表
    ListNode* former = NULL;
    ListNode* mid = list2;
    ListNode* latter = NULL;

    while(mid !=NULL){
        latter = mid->next;
        mid->next = former;
        former = mid;
        mid = latter;
    }

    list1 = head;

    while((former !=NULL) &&(list1 !=NULL)){
        if(former->val != list1->val){
            return false;
        }
        former = former->next;
        list1 = list1->next;
    }
    return true;
}

main函数:

int main(){
    ListNode* head = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(3);
    ListNode* node5 = new ListNode(2);
    ListNode* node6 = new ListNode(1);
    head->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
	Solution* s = new Solution();
    cout<<"原链表:";
    printList(head);
    //判断是否为回文链表
    bool palindromeFlag = s-> findPalindrome(head);//空链表和只有一个节点的链表也为回文链表
    if(palindromeFlag == true){
        cout<<"该链表为回文链表";
    }else{
        cout<<"该链表不为回文链表";
    } 
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值