数据结构OJ 简单算法题

移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

复制代码
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

该题就是就任=让我们先构建一个删除和一个识别val 的数值来实现。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* removeElements(struct ListNode* head, int val) {
    if(head==NULL)
    {
        return NULL;
    }
    struct ListNode* node=head,*ptr=head->next;
    while(ptr != NULL)
    {
        if(ptr->val==val)
        {
            node->next=ptr->next;
            free(ptr);
            ptr=node->next;
        }
        else
        {
            node=node->next;
            ptr=ptr->next;
        }
    }
    if(head->val==val)
    {
        ptr=head;
        head=head->next;
        free(ptr);
        ptr=NULL;
    }
    return head;
}

解题思路:

1、判断是否为空指针,如果为空指针就返回NULL

2、声明一个节点指针和一个要被删除的预留指针。题目给的头指针不动,便于后面要返回数值的时候可以之直接返回头指针。

注意一定不要先从头指针入手,从头指针的第二个元素进行操作,头指针时应该链表的最重要的位置,如果直接删除的head就会直接变成野指针,返回时就会导致野指针的错误访问。

3、构建循环体

(1)判断条件ptr 到NULL就退出循环。

(2)过程判断是否为val所要的数值,否就正常移动位置

4、判断头指针是否为对应数值。

5、返回头指针。

链表的中间节点(快慢指针)

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

就如标题所说的利用快慢指针,什么快慢指针呢?就是字面意思,应该指针走的块,一个指针走的慢,那如何用快慢指针解决问题呢?例如在一个跑道上,小明的速度为V,小东的速度为2V,那么在相同时间内,小明总差小东的一半距离。所以这就是解题思路。

声明两个指针,一个指针每次走一步叫慢指针,一个指针每次走两步叫快指针。当快指针到达空指针时,慢指针的位置就刚好就是链表的中间位置。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* v1,*v2;//快慢指针
    v1=v2=head;

    while(v2!=NULL)
    {
        if(v2->next==NULL)//防止访问野指针
            v2=v2->next;
        else
        {
            v2=v2->next->next;
            v1=v1->next;
        }
    }
    return v1;
}

返回倒数K个节点

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

**注意:**本题相对原题稍作改动

示例:

复制代码
输入: 1->2->3->4->5 和 k = 2
输出: 4

这个题目偏简单,计算总长度,在对应位置结束就行。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

int kthToLast(struct ListNode* head, int k) {
    struct ListNode* p=head;
    int count=0;
    while(p)
    {
        p=p->next;
        count++;
    }
    while(count!=k)
    {
        head=head->next;
        count--;
    }
    return head->val;
}

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if(head==NULL)
    {
        return NULL;
    }
    struct ListNode* lost=NULL,*node=head;
    head=head->next;
    while(head!=NULL)
    {
        node->next=lost;
        lost=node;
        node=head;
        head=head->next;
    }
    node->next=lost;
    return node;
}

解题思路:

1、先判断指针为NULL

2、构建以左右中三节点,因为有现成的head 的头指针,就再多声明两个指针就可以,lost node head,三个其中lost =NULL,node=head,head=head->next ;

3、构建循环,依次对应的指针交换方向。head==NULL退出循环

4、因为因为循环到最后还有lost 和node 两个节点还没有交换方向,进行单独处理。

5,return node;

合并两个有序的链式表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    //判断为空指针
    if(list1==NULL && list2==NULL)
    {
        return NULL;
    }
    else if(list1 && !list2)
    {
        return list1;
    }
    else if(list2 && !list1)
    {
        return list2;
    }
    //初始化
    struct ListNode* head=NULL,*pnode=NULL;
    //定节点和头节点
   if(list1->val < list2->val)
   {
        pnode=list1;
        list1=list1->next;
   }
    else
    {
        pnode=list2;
        list2=list2->next;
    }
    head=pnode;
    pnode->next=NULL;//从新创出一条单链表,但不用malloc的申请空间
    while(list1 && list2)
    {
        if(list1->val < list2->val)//判断大小
        {
            pnode->next=list1;
            list1=list1->next;
        }
        else
        {
            pnode->next=list2;
            list2=list2->next;
        }
        pnode=pnode->next;//向前走
    }
    if(list1)//漏下的节点
        pnode->next=list1;
    if(list2)
        pnode->next=list2;
    return head;
}

解题思路:

我的思路不一定时最优解的,因为两个指针交叉做的时候脑子有点过载了,所以就用来一个最不容易解法就是单独再起一个链表,当然可以用不到malloc 的申请空间,就时把他们当成搭积木一样从两个链表里拆下一点,拼接到新的链表里,如果其中有一个链表已经被拆完了剩下的链表的接上去就可以。

当然还有例外一种做法就是,先通过比较头节点的大小选好要哪一个链表作为主链表,选好主链表比较的第n和第n+1个是不是在两数值之间的数值大小,时就插入节点......到如果没有节点可以插入就退出,如果主链表到头了,就直接把剩下的节点加进去。

相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int count(struct ListNode** head)
{
    if(*head==NULL)
    {
        return 0;
    }
    return 1+count(&((*head)->next));
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int A=count(&headA),B=count(&headB);

    if(A>B)
    {
        for(int i=0;i<(A-B);i++)
            headA=headA->next;
    }
    else if(A<=B)
    {
        for(int i=0;i<(B-A);i++)
            headB=headB->next;
    }
    
    while(headA!=headB)
    {
        if(!headA || !headB)
        {
            return NULL;
        }
        headA=headA->next;
        headB=headB->next;
    }
    return headA;
    
}

解题思路:

计算两个链表的长度,对齐长度,向前走遇到相同地址就访问,没有遇到就返回NULL

重排链表

给定一个单链表 L的头节点 head ,单链表 L 表示为:

L0 → L1 → ... → Ln-1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → ...

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middle_node(struct ListNode** head)
{
    struct ListNode *v1, *v2, *cur;
    v1=v2=cur=*head;
    while(v2->next!=NULL && v2->next->next != NULL)
    {
            v1=v1->next;
            v2=v2->next->next;
    }
    cur=v1;
    v1=v1->next;
    cur->next=NULL;
    return v1;
}

struct ListNode* fun(struct ListNode** head)
{
    struct ListNode* nodenext,*lostnode=NULL;
    nodenext=(*head)->next;
    while(nodenext!=NULL)
    {
        (*head)->next=lostnode;
        lostnode=*head;
        *head=nodenext;
        nodenext=nodenext->next;
    }
    (*head)->next=lostnode;
    return *head;
}

void reorderList(struct ListNode* head){
    if(head==NULL || head->next==NULL)
        return;
    struct ListNode* phead=head;
    struct ListNode* mid=middle_node(&phead);
    struct ListNode* ret=fun(&mid),*node=NULL;
    while(head && ret)
    {
        node=head;
        head=head->next;
        node->next=ret;
        
        node=ret;
        ret=ret->next;
        node->next=head;
    }

    return;
}

解题思路:

利用前面学到的算法,找中间节点(利用快慢指针),反转链表,并和两个链式表。

环型链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

解题思路:

还是利用快慢指针,因为快指针跑得快,慢指针一定会被套圈遇到

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    if(head == NULL || head->next == NULL )
    {
        return false;
    }
    struct ListNode* v1,*v2;
    v1=v2=head;
    while(v2 != NULL && v2->next != NULL)
    {
        v1=v1->next;
        v2=v2->next->next;
        if(v1==v2)
        {
            return true;
        }
    }
    return false;
}

变形:

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 )。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改链表。

要求指针返回地址

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    if(head == NULL || head->next == NULL )
    {
        return NULL;
    }
    struct ListNode* v1,*v2;
    v1=v2=head;
    while(v2 != NULL && v2->next != NULL)
    {
        v1=v1->next;
        v2=v2->next->next;
        if(v1==v2)
        {
            v2=head;
            while(v1!=v2)
            {
                v1=v1->next;
                v2=v2->next;
            }
            return v1;
        }
    }
    return NULL;
}

还是用刚才的代码进行改写。

要只知道一个公式在一个完美的环的时候快指针和慢指针相遇到的时候位置一定时在起点位置,非完美环的时候,多余出来的位置就是,在环的起点位置往后和快慢指针相遇的位置,所以在相遇到后,把快指针从链表的头指针开时与慢指针匀速前进,就一定遇到位置和环的起点重合。

感谢观看!!!

悠仁さん

相关推荐
WBluuue1 小时前
数据结构与算法:树上启发式合并
数据结构·c++·算法·启发式算法
不是光头 强1 小时前
feign-list-param-crash-cpp
java·数据结构·list
计算机安禾1 小时前
【算法设计与分析】第40篇:空间数据结构:KD树与四叉树的查询分析
数据结构·算法
努力努力再努力wz2 小时前
【C++高阶数据结构系列】:跳表 SkipList 详解:多层索引、随机晋升与C++ 完整实现(附跳表实现的源码)
开发语言·数据结构·数据库·c++·redis·缓存·skiplist
代码中介商2 小时前
图论入门:从基础到遍历算法
数据结构·算法·图论
飞天狗1112 小时前
2024第十五届蓝桥杯c/c++B组国赛题解
c语言·数据结构·c++·算法·蓝桥杯
2401_8724187810 小时前
算法入门:数据结构-堆
数据结构·算法
不会就选b15 小时前
数据结构之顺序表和链表的OJ题(上)
数据结构·链表
啦啦啦啦啦zzzz17 小时前
数据结构:二叉树的线索化
数据结构·算法