首页 > 其他分享 >链表操作

链表操作

时间:2023-07-15 15:23:43浏览次数:35  
标签:head ListNode struct return next 链表 操作 NULL

1.单链表逆序,要求在原链表数据不改变的情况下进行逆序

方法一:

新建一个头节点,将链表中的元素一个一个放入新的头节点

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* n = malloc(sizeof(struct ListNode));
    n->next = NULL;
    struct ListNode* p = NULL;
    for(;head!= NULL;)
    {
        p = head;
        head=head->next;
        p->next = n->next;
        n->next = p;
    }
    return n->next;
}

方法二:

直接在原链表操作,遍历节点,将每个节点放在链表最前面(在原来的链表有头节点的情况下)

struct ListNode* ReverseList(struct ListNode* head){
    if(!head||!head->next)return head;
    struct ListNode* node = head;
    for(struct ListNode* n = head->next;n->next!=NULL;)
    {
        struct ListNode* temp = n->next;
        n->next = temp->next;
        temp->next = head->next;
        node->next = temp;
    }
    return head;
}

1:    head(node)    n    next(temp)    next    next    next

0                   1        2                3            4            5

head(node)    next(temp)    n    next    next    next

0                    2                    1       3        4            5

2:    head(node)    next    n    next(temp)    next    next

0                    2        1        3                    4            5

head(node)    next(temp)    next     n   next    next

0                         3                 2        1      4        5

2.找出单链表中倒数第n个节点

方法一:反转链表,找到第n个节点(题目要求,如果对于该节点的next无要求)

struct ListNode* FindKthToTail(struct ListNode* pHead, int k ){
    ReverseList(struct ListNode* pHead);
    while(--k)//如果有头节点,换成k--
    {
        pHead = pHead->next;
    }
    return pHead;
}

方法二:遍历一遍,求出链表共多长,再计算出要next的次数,找到该节点(无头节点版本)

struct ListNode* FindKthToTail(struct ListNode* pHead, int k ){
    if(!head)return pHead;
    int i = 1;
    struct ListNode* node = pHead;
    while(node = node->next)++i;
    i -= k;
    if(i > 0)return NULL;
    while(i--)
    {
        pHead = pHead->next;
    }
    return pHead;
}

方法三:按照k的值,在next k次后,再从头开始用新指针遍历,直到旧指针遍历到NULL结束(无头节点)

struct ListNode* FindKthToTail(struct ListNode* pHead, int k ){
    if(!head)return pHead;
    struct ListNode* old = pHead; 
    struct ListNode* n = pHead; 
    while(--k)
    {
        old = old->next;
        if(NULL == old)return NULL;
    }
    while(NULL != old)
    {
        old = old->next;
        n = n->next;
    }
    return n;
}

3.判断单链表中是否有环

方法一:覆盖链表中的值(会破坏链表),如果又一次遇到覆盖过的节点,则有环

bool hasCycle(struct ListNode *head) {
    if(head == NULL)return false;
    while(head->next)
    {
        head->val = 'bjfuvth';
        head = head->next;
        if(head->next->val == 'bjfuvth')return true;
    }
    return false;
}

方法二:利用快慢指针 p1 = p1->next;p2 = p2->next;如果遍历到NULL,则没有环,如果遍历到两个指针相遇,则有环(例如地球是圆的)

bool hasCycle(struct ListNode *head) {
    if(head == NULL)return false;
    struct ListNode* q = head;
    struct ListNode* p = q;
    while(q!= NULL && q->next!= NULL)
    {
        q = q->next->next;
        p = p->next;
        if(q == p)return true;
    }
    return false;
}

4、找出环形单链表的环的入口位置

方法一:

  • 1.第一次相遇,slow = nb
  • 2.a+nb = 入口点
  • 3.slow再走a = 入口 = head走到入口 = a
  • 4.由3得出,起始距离入口 = 第一次相遇位置 + a
struct ListNode *detectCycle(struct ListNode *head) {
    if(!head)return NULL;
    struct ListNode* fast = head;
    struct ListNode* slow = head;
     while(fast!= NULL && fast->next!= NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)break;
    }
    if(fast == NULL)return;
    fast = head;
    while(fast != slow)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}

5.合并两个有序的单链表生成一个有序的单链表

方法一:直接对两个链表进行比较,插入

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL)return list2;
    if(list2 == NULL)return list1;
    if(list1->val > list2->val)return mergeTwoLists(list2,list1);
    struct ListNode* n1 = list1->next;
    struct ListNode* n2 = list2;
    struct ListNode* n = list1;
    while(1)
    {
        if(NULL == n1)
        {
            n->next = n2;
            break;
        }
        if(NULL == n2)
        {
            n->next = n1;
            break;
        }
        if(n1->val > n2->val)
        {
            n->next = n2;
            n = n2;
            n2 = n2->next;
        }
        else
        {
            n->next = n1;
            n = n1;
            n1 = n1->next;
        }
    }
    return list1;
}

方法二:递归

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL)return list2;
    if(list2 == NULL)return list1;
    if(list1->val < list2->val)
    {
        list1->next = mergeTwoLists(list1->next,list2);
        return list1;
    }
    else
    {
        list2->next = mergeTwoLists(list1,list2->next);
        return list2;
    }
}

6.判断两个单链表是否是Y型链表

方法一:指针q在链表遍历完继续遍历B链表,指针p在链表B遍历完继续遍历链表A

相当于q指针遍历了链表A+链表B,p指针遍历了链表B+链表A。遍历的长度是一样的。

如果没有相交点,最后都会回到NULL,p == q,返回NULL

如果有相交点,把AB链表分解为,相交点前的链表A和链表B,和相交点后的链表C

指针q遍历顺序 = A->C->B

指针p遍历顺序 = B->C->A

最后都会在相交点相遇(走到尽头见不到你,于是走过你来时的路,等到相遇时才发现,你也走过我来时的路。)

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(!headA || !headB)return NULL;
    struct ListNode* q = headA;
    struct ListNode* p = headB;
    while(q != p)
    {
        q = q ? q->next:headB;
        p = p ? p->next:headA;
    }
    return p;
}

标签:head,ListNode,struct,return,next,链表,操作,NULL
From: https://www.cnblogs.com/bigflyny/p/17556170.html

相关文章

  • AI绘画Stable Diffusion实战操作: 62个咒语调教-时尚杂志封面
    今天来给大家分享,如何用sd简单的咒语输出好看的图片的教程,今天做的是时尚杂志专题,话不多说直入主题。还不会StableDiffusion的基本操作,推荐看看这篇保姆级教程:AI绘画:StableDiffusion终极炼丹宝典:从入门到精通飞书原文链接:AI绘画StableDiffusion实战操作:62个咒语调教-时尚杂......
  • C语言文件操作及字符串学习记录
    #include<stdio.h>#include<errno.h>#include<string.h>#include<stdlib.h>#include<stddef.h>//externinterrno;#if0intcountSpace(char*s){inti=0;intcount=0;while(s[i]!='\0'){......
  • DBGridEh 基本操作
    导出到Excel等文件类型1.导入导出引用单元useDBGridEhImpExp;类型说明类型名称说明TDbGridEhExportAsText导出到文本文件TDbGridExportAsUnicodeText导出到Unicode文本TDbGridEhExportAsCSV导出到CSVTDbGridEhAsHtml导出到HTMLTDBGridEhAsRTF......
  • Rxjs tap 操作符的使用场景介绍
    RxJS的tap操作符是一个非常有用的工具,它允许我们“查看”Observable流中的数据,同时不会对数据流产生任何影响。换句话说,它是一种副作用(sideeffect)操作符,允许我们在不更改主要数据流的情况下执行一些额外的操作,如日志记录、调试或其他副作用。在详细讨论tap操作符的使用场......
  • Rxjs 里 Observable 对象的 tap 操作
    在RxJS中,tap操作符是一种用于在Observable流中插入额外的副作用操作的工具。它允许我们在数据流中进行调试、记录日志、执行辅助操作等,而不会改变原始的Observable数据流。tap操作符接收一个回调函数,该函数会在每个值通过Observable时被调用。tap操作符的使用场景有很......
  • day03 链表基本操作
    前置知识,链表数据结构1.移除链表元素移除链表元素不难,只需要把前一个结点的下一节点指向下一个节点的下一节点如果当前遍历的节点与所给值相等,则需要移除此元素,移除元素是将上一节点的next域设置为当前节点的next,当前节点后移一位如果当前遍历的节点值不等于所给值,则前驱......
  • 50.new操作符具体干了什么呢如何实现
    50.new操作符具体干了什么呢?如何实现?//(1)首先创建了一个新的空对象//(2)设置原型,将对象的原型设置为函数的prototype对象。//(3)让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)//(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返......
  • 81.哪些操作会造成内存泄漏
    81.哪些操作会造成内存泄漏?相关知识点:1.意外的全局变量2.被遗忘的计时器或回调函数3.脱离DOM的引用4.闭包回答:第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。第二种情况是我们设置了setInterval定时器,而......
  • IntelliJ IDEA中我最爱的10个快捷操作
    前言IntelliJIDEA提供了一些Java的快捷键,同样也可以帮助我们提高日常的开发效率。关于这些快捷操作,你知道那几个呢?1.psvm/main快速生成main()方法在日常开发中,我们经常需要写main()方法,这时候您也可以使用main或者psvm命令快速地帮助我们创建出main()方法。2.sout快速生成print......
  • docker镜像和容器操作命令
    1、镜像操作1.1searchdockersearch<镜像名称>dockersearchhello-world在docker仓库搜索指定的镜像docker官网提供了一个页面,来进行搜索需要安装的软件的镜像的命令https://index.docker.io/search?q=&type=image通过输入不完全的镜像名称,可用得到相关的镜像列表......