leetcode经典题系列------链表

定义如下:

struct ListNode{
    int val;
    ListNode *next;
    ListNode(int x):val(x),next(NULL){}
};

第一题:链表翻转 easy

ListNode * reverseList(ListNode * head)
{
    ListNode *new_head=NULL;//指向新链表头节点的指针
    while(head){
        ListNode *next=  head->next;//备份head->next
        head->next=new_head;//更新hea睨zhid->next;
        new_head=head;//移动new_head;
        head=next;//遍历链表
    }
    return new_head;//返回新链表头节点
}

第二题 链表翻转 变式

描述:

已知链表头节点指针head,将链表从位置m到n逆序。(不申请额 外空间)
比如m=2,n=4,表示从2个节点到第四个节点逆向 1<=m<=n<=链表长度

思路:

寻找关键点
1 逆置段头节点的前驱
2 逆置前头节点变为你只后尾节点
3 逆置前尾节点变为你只后头节点
4 逆置段尾节点的后继
步骤一:将head向前移动m-1个位置,找到开始逆置的节点,记录该节点的前驱和该节点
步骤二:从head开始,逆置change_len=n-m+1d个节点
步骤三:将pre_head与new_head连接,modify_list_tail与head相连

ListNode * reverseBetween(ListNode *head,int m ,int n){
    int change_len=n-m+1;//计算需要逆置的节点个数
    ListNode *pre_head=NULL;//初始化开始逆置的节点的前驱
    ListNode *result=head;//最终转换后的链表头节点,非特殊情况即为head
    while (head&&--m) {//将head向前移动m-1个位置
        pre_head=head;
        head=head->next;
    }
    //将modify_list_tail指向当前的head,即逆置后的链表尾
    ListNode *modify_list_tail=head;
    ListNode * new_head=NULL;
    while (head&&change_len) {//逆置change_len个节点
        ListNode *next=head->next;
        head->next=new_head;
        new_head=head;
        head=next;
        change_len--;//每完成一个节点逆置,change_len--;
    }
    modify_list_tail->next=head;//连接逆置后的链表尾与逆置段的后一个节点
    //如果pre_head不为空,说明不是从第一个节点开始逆置的 m>1
    //将逆置链表开始的节点前驱与逆置后的头节点连接
    if(pre_head){
       
        pre_head->next=new_head;
    }
    else{
         //如果pre_head为空,说明m=1从第一个节点逆置,结果为逆置后的头节点
        result=new_head;
    }
    return result;
}

第三题 求两个链表的交点

描述:

已知链表A的头节点指针headA,链表B的头节点指针headB,两 个链表相交,求两链表交点对应的节点

要求:

1.如果两个链表没有交点,则返回NULL
2.在求交点的过程中,不可以破坏链表的结构或者修改链表的数据域
3.可以确保传入的链表A与链表B没有任何环
4*.实现算法尽可能使时间复杂度O(n),空间复杂度O(1)

思路:

步骤一:计算headA链表长度,计算headB链表长度,较长的链表多出的长度
步骤二:将较长链表的指针移动到和较短链表指针对其的位置
步骤三:headA和headB同时移动,当两指针h指向同一个节点时,找到了

//计算链表长度
int get_list_length(ListNode *head){
    int len=0;
    while (head) {
        len++;
        head=head->next;
    }
    return len;
}
//向前移动
ListNode *forward_long_list(int long_len,int short_len,ListNode *head){
    int delta=long_len-short_len;
    while (head&&delta) {
        head=head->next;
        delta--;
    }
    return head;
}
//得到交点
ListNode *getIntersectionNode(ListNode *headA,ListNode *headB){
    int list_A_length=get_list_length(headA);
    int list_B_length=get_list_length(headB);
    if(list_A_length>list_B_length){
        headA=forward_long_list(list_A_length, list_B_length, headA);
    }
    else{
        headB=forward_long_list(list_B_length, list_A_length, headB);
    }
    while (headB&&headA) {
        if(headB==headA){
            return headA;
        }
        headA=headA->next;
        headB=headB->next;
    }
    return NULL;
    
}

第四题:链表求环

描述:

已知链表中可能存在环,若有环返回环起始节点,否则返回NULL。

思路:

快慢指针赛跑

先求出相遇点,然后根据数学公式
从相遇点出发和从head出发,相遇点就是 环入口点

ListNode *detectCycle(ListNode *head){
    ListNode *fast=head;
    ListNode * slow=head;
    ListNode *meet=NULL;
    while (fast) {
        slow=slow->next;
        fast=fast->next;
        if(!fast){
            return NULL;
        }
        fast=fast->next;
        if(fast==slow){
            meet=fast;
            break;
        }
    }
    if(meet==NULL){
        return NULL;
    }
    while (head&&meet) {
        if(head==meet){
            return head;
        }
        head=head->next;
        meet=meet->next;
    }
    return NULL;
}

第五题:链表划分

描述:

已知链表头指针head与数值x,将所有小于x的节点放在大于或等于x 的节点前,且保持这些节点的原来的相对位置。

思路:

巧用临时头节点,然后用链表尾插法

ListNode * partition(ListNode *head,int x){
    ListNode less_head(0);
    ListNode more_head(0);
    ListNode *less_ptr=&less_head;
    ListNode *more_ptr=&more_head;
    while (head) {
        if(head->val<x){
            less_ptr->next=head;
            less_ptr=head;
        }
        else{
            more_ptr->next=head;
            more_ptr=head;
        }
        head=head->next;
    }
    less_ptr->next=more_head.next;
    more_ptr->next=NULL;
    return less_head.next;
}

第六题:复杂链表的深度拷贝

描述:

已知一个复杂的链表,节点中有一个指向本链表任意某个节点的随机指 针(也可以为空),求这个链表的深度拷贝。

思路:

通过Map映射,这里用2个Map

第一个Map
遍历老链表,Map[节点地址]=节点索引位置 保存
第二个Map
遍历老链表,Map[节点位置]=新链表节点地址

struct RandomListNode{
    int label;
    RandomListNode *next,*random;
    RandomListNode(int x):label(x),next(NULL),random(NULL){}
};

RandomListNode * copyRandomList(RandomListNode *head)
{
    std::map<RandomListNode*,int> node_map;//地址到节点位置的map
    std::vector<RandomListNode*> node_vec;//使用vector根据存储节点位置访问的地址
    RandomListNode *ptr=head;
    int i=0;
    while (ptr) {
        node_vec.push_back(new RandomListNode(ptr->label));
        node_map[ptr]=i;
        ptr=ptr->next;
        i++;
    }
    node_vec.push_back(0);//多加一个元素,后面i+1的时候方便处理
    ptr=head;
    i=0; //再次遍历原始列表 连接新链表的next指针,random指针
    while (ptr) {
        node_vec[i]->next=node_vec[i+1];//连接新链表的next指针
        if(ptr->random){
            int id=node_map[ptr->random];//获取索引
            node_vec[i]->random=node_vec[id];
        }
        ptr=ptr->next;
        i++;
    }
    return node_vec[0];
}

第七题:2个链表的合并

描述:

已知两个已排序链表头节点指针l1与l2,将这两个链表合并,合并后仍为 有序的,返回合并后的头节点。

思路:

借助头节点

ListNode *mergeTwoLists(ListNode *l1,ListNode *l2){
    ListNode temp_head(0);
    ListNode *pre=&temp_head;
    while (l1&&l2) {
        if(l1->val < l2->val){
            pre->next=l1;
            l1=l1->next;
        }
        else{
            pre->next=l2;
            l2=l2->next;
        }
        pre=pre->next;
    }
    if(l1){
        pre->next=l1;
    }
    if(l2){
        pre->next=l2;
    }
    return temp_head.next;
}

第八题:多个排序链表的合并

思路:

采用分治法效率最高

算法负责度 为O(kNlogk),k为链表个数,n为每个链表的长度
比如 每个链表长度为3,k为8,第一轮,进行k/2=4次比较(8/2=4,4/2=2),每次处理2n个数字,也就是6个(2个链表为6)
第二轮进行8/4=2次比较,每次比较4n个数字,第三轮是最后一轮,进行1次比较,处理8*3个数

ListNode* mergeKLists(std::vector<ListNode*>&lists)
{
    if(lists.size()==0){
        return NULL;
    }
    if(lists.size()==1){
        return lists[0];
    }
    if(lists.size()==2){
        return mergeTwoLists(lists[0], lists[1]);
    }
    int mid=(int)lists.size()/2;
    std::vector<ListNode*>sub1_lists;
    std::vector<ListNode*>sub2_lists;//拆分lists为两个子lists
    for (int i=0; i<mid; i++) {
        sub1_lists.push_back(lists[i]);
    }
    for (int i=mid; i<lists.size(); i++) {
        sub2_lists.push_back(lists[i]);
    }
    ListNode *l1=mergeKLists(sub1_lists);
    ListNode *l2=mergeKLists(sub2_lists);
    return mergeTwoLists(l1, l2);//分制处理
}

第九题:交换一个单链表 两两相邻的两个元素

思路1:借助3个指针进行移动

ListNode *swapPairs(ListNode *head){
    if(head==NULL)
        return NULL;
    ListNode preHead(0);
    preHead.next=head;
    ListNode *left=&preHead;
    ListNode *mid=head;
    if(head->next==NULL){
        return head;
    }
     ListNode *right=head->next;
    while (mid&&mid->next) {
        mid->next=right->next;
        right->next=mid;
        left->next=right;

        left=mid;
        mid=left->next;
        if(mid){
            right=mid->next;
        }
    }
    return  preHead.next;
    
}

思路2:递归

借助temp保存节点的下一个节点,然后本节点的next指向递归函数返回的指针,最后temp的next指向head

ListNode *swapPairs(ListNode *head){

    if(!head) return NULL;
             if(!head->next) return head;
             ListNode* temp=head->next;
             head->next=swapPairs(temp->next);
             temp->next=head;
             return temp;
}

猜你喜欢

转载自www.cnblogs.com/xiaonanxia/p/10137695.html