单链表的相关OJ题

单链表的相关问题

链表经典算法OJ题目

移除链表元素

题目链接:移除链表元素

/**
 * Definition for singly-linked list.
 * struct ListNode 
 * {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) 
{
    //创建一个空链表
    struct ListNode* newHead = NULL;
    struct ListNode* newTail = NULL;

    //遍历原链表
    struct ListNode* pcur = head;
    while(pcur)
    {
        //找到值不为value的节点,尾插到新链表中
        if(pcur->val != val)
        {
            //链表为空
            if(newHead == NULL)
            {
                newHead = newTail = pcur;
            }
            //链表不为空
            else
            {
                newTail->next = pcur;
                newTail = newTail->next;
            }
        }
        pcur = pcur->next;
    }
    if(newTail)
    {
        newTail->next = NULL;
    }
    return newHead;
}

反转链表

题目链接:反转链表

/**
 * Definition for singly-linked list.
 * struct ListNode 
 * {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) 
{
    if(head == NULL)
    {
        return head;
    }
    
    //创建三个指针
    struct ListNode* n1 = NULL;
    struct ListNode* n2 = head;
    struct ListNode* n3 = head->next;
    while(n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if(n3)
        {
            n3 = n3->next;
        }
    }
    return n1;
}

合并两个有序链表

题目链接:合并两个有序链表

/**
 * Definition for singly-linked list.
 * struct ListNode 
 * {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    if(list1  == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }

    struct ListNode* l1 = list1;
    struct ListNode* l2 = list2;
    
    //创建新的链表
    struct ListNode* newHead = NULL;
    struct ListNode* newTail = NULL;
    newHead = newTail = (struct ListNode*)malloc(sizeof(struct ListNode));

    while(l1 && l2)
    {
        if(l1->val < l2->val)
        {
            //l1尾插
            if(newHead == NULL)
            { 
                newHead = newTail = l1;
            }
            else
            {
                newTail->next = l1;
                newTail = newTail->next;
            }
            l1 = l1->next;
        }
        else
        {
            //l2尾插
             if(newHead == NULL)
            { 
                newHead = newTail = l2;
            }
            else
            {
                newTail->next = l2;
                newTail = newTail->next;
            }
            l2 = l2->next;
        }
    }
    //跳出循环代表至少有一个为空
    if(l1)
    {
        newTail->next = l1;
    }
    if(l2)
    {
        newTail->next = l2;
    }
    //手动释放掉动态申请的空间
    struct ListNode* ret = newHead->next;
    free(newHead);
    newHead = NULL;
    return ret;
}

链表的中间结点

题目链接:链表的中间结点

/**
 * Definition for singly-linked list.
 * struct ListNode 
 * {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head) 
{
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

环形链表的约瑟夫问题

题目链接:环形链表的约瑟夫问题

著名的Josephus问题
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param n int整型 
 * @param m int整型 
 * @return int整型
 */

typedef struct ListNode ListNode;

ListNode* BuyNode(int x)//创建节点
{
    ListNode* node = (ListNode*) malloc(sizeof(ListNode));
    if(node == NULL)
    {
        perror("malloc failed");
        exit(1);
    }
    node->val = x;
    node->next = NULL;
    return node;
}

ListNode* CreateListNode(int n)//创建带环链表
{
    //先创建头节点
    ListNode* phead = BuyNode(1);
    ListNode* ptail = phead;
    for(int i = 2; i <= n; i++)//尾插
    {
        ptail->next = BuyNode(i);
        ptail = ptail->next;
    }
    //首尾相连
    ptail->next = phead;
    return ptail;
}

int ysf(int n, int m ) 
{
    //1. 根据n创建带环链表
    ListNode* prev = CreateListNode(n);
    ListNode* pcur = prev->next;

    //2. 报数
    int count = 1;
    while(pcur->next != pcur)//指针指向自己,证明只剩一个
    {
        if(count == m)
        {
            //销毁pcur节点
            prev->next = pcur->next;
            free(pcur);
            pcur = prev->next;
            count = 1; //重新报数
        }
        else 
        {
            prev = pcur;
            pcur = pcur->next;
            count++;
        }
    }
    return pcur->val;
}

基于单链表再实现通讯录项目

contact.h

#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{
    char name[NAME_MAX];
    char sex[SEX_MAX];
    int age;
    char tel[TEL_MAX];
    char addr[ADDR_MAX];
}PeoInfo;
//初始化通讯录
void InitContact(contact** con);//实际调用的是链表的初始化接口(可以简单做一个头结点的初
//添加通讯录数据
void AddContact(contact** con);// 链表尾插/头插
//删除通讯录数据
void DelContact(contact** con);//链表的删除指定位置的数据
//展示通讯录数据
void ShowContact(contact* con);//链表的打印
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);

SList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"contact.h"
typedef struct PersonInfo SLTDataType;
//typedef int SLTDataType;
typedef struct SListNode {
    struct SListNode* next;
    SLTDataType data;
}SLTNode;
void SLTPrint(SLTNode* phead);
//头部插入删除/尾部插入删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);

contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
#include"SList.h"
void LoadContact(contact** con) 
{
    FILE* pf = fopen("contact.txt", "rb");
    if (pf == NULL) {
        perror("fopen error!\n");
        return;
    }
    //循环读取文件数据
    PeoInfo info;
    while (fread(&info, sizeof(info), 1, pf))
    {
        SLTPushBack(con, info);
    }
    printf("历史数据导入通讯录成功!\n");
}
void InitContact(contact** con) 
{
    LoadContact(con);//把本地的通讯录数据导入到链表结构
}
void AddContact(contact** con) 
{
    PeoInfo info;
    printf("请输入姓名:\n");
    scanf("%s", &info.name);
    printf("请输入性别:\n");
    scanf("%s", &info.sex);
    printf("请输入年龄:\n");
    scanf("%d", &info.age);
    printf("请输入联系电话:\n");
    scanf("%s", &info.tel);
    printf("请输入地址:\n");
    scanf("%s", &info.addr);
    SLTPushBack(con, info);
    printf("插入成功!\n");
}
contact* FindByName(contact* con, char name[]) 
{
    contact* cur = con;
    while (cur)
    {
        if (strcmp(cur->data.name, name) == 0) 
        {
            return cur;
        }
        cur = cur->next;
    }
    return NULL;
}
void DelContact(contact** con) 
{
    char name[NAME_MAX];
    printf("请输入要删除的用户姓名:\n");
    scanf("%s", name);
    contact* pos = FindByName(*con, name);
    if (pos == NULL) 
    {
        printf("要删除的用户不存在,删除失败!\n");
        return;
    }
    SLTErase(con, pos);
    printf("删除成功!\n");
}
void ShowContact(contact* con) 
{
    printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "联系电话", "地址");
        contact * cur = con;
    while (cur)
    {
        printf("%-10s %-4s %-4d %15s %-20s\n",
            cur->data.name,
            cur->data.sex,
            cur->data.age,
            cur->data.tel,
            cur->data.addr);
        cur = cur->next;
    }
}
void FindContact(contact* con) 
{
    char name[NAME_MAX];
    printf("请输入要查找的用户姓名:\n");
    scanf("%s", name);
    contact* pos = FindByName(con, name);
    if (pos == NULL) 
    {
        printf("要查找的用户不存在,查找失败!\n");
        return;
    }
    printf("查找成功!\n");
    printf("%-10s %-4s %-4d %15s %-20s\n",
        pos->data.name,
        pos->data.sex,
        pos->data.age,
        pos->data.tel,
        pos->data.addr);
}
void ModifyContact(contact** con) 
{
    char name[NAME_MAX];
    printf("请输入要修改的用户名称:\n");
    scanf("%s", &name);
    contact* pos = FindByName(*con, name);
    if (pos == NULL) 
    {
        printf("要查找的用户不存在,修改失败!\n");
        return;
    }
    printf("请输入要修改的姓名:\n");
    scanf("%s", pos->data.name);
    printf("请输入要修改的性别:\n");
    scanf("%s", pos->data.sex);
    printf("请输入要修改的年龄:\n");
    scanf("%d", &pos->data.age);
    printf("请输入要修改的联系电话:\n");
    scanf("%s", pos->data.tel);
    printf("请输入要修改的地址:\n");
    scanf("%s", pos->data.addr);
    printf("修改成功!\n");
}
void SaveContact(contact* con) 
{
    FILE* pf = fopen("contact.txt", "wb");
    if (pf == NULL) 
    {
        perror("fopen error!\n");
        return;
    }
    //将通讯录数据写入文件
    contact* cur = con;
    while (cur)
    {
        fwrite(&(cur->data), sizeof(cur->data), 1, pf);
        cur = cur->next;
    }
    printf("通讯录数据保存成功!\n");
}
void DestroyContact(contact** con) 
{
    SaveContact(*con);//在通讯录销毁之前,先把历史数据保存到本地文件中contact.txt
    SListDesTroy(con);
}

拓展

返回倒数第 k 个节点

题目链接:返回倒数第 k 个节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

//方法一:暴力解法
/*
int kthToLast(struct ListNode* head, int k)
{
    int ListSize = 0;
    struct ListNode* pcur = head;
    while(pcur->next != NULL)
    {
        ListSize++;
        pcur = pcur->next;
    }
    pcur = head;
    for(int i = 0; i <= ListSize - k; i++)
    {
        pcur = pcur->next;
    }
    return pcur->val;
}
*/

//方法二:快慢指针
//满足要求:时间复杂度O(1);只能遍历一次
int kthToLast(struct ListNode* head, int k)
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    //快指针先走k步
    while(k--)
    {
        fast = fast->next;
    }
    //同时走
    while(fast)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow->val;
}

链表的回文结构

题目链接:链表的回文结构

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList 
{
public:
    struct ListNode* middleNode(struct ListNode* head)
    {
        struct ListNode* fast = head;
        struct ListNode* slow = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
    struct ListNode* reverseList(struct ListNode* head)
    {
        struct ListNode* pcur = head;
        struct ListNode* newhead = NULL;
        while(pcur)
        {
            struct ListNode* next = pcur->next;
            //头插
            pcur->next = newhead;
            newhead = pcur;

            pcur = next;
        }
        return newhead;
    }
    bool chkPalindrome(ListNode* A) 
    {
        struct ListNode* mid = middleNode(A);
        struct ListNode* rmid = reverseList(mid);
        while(rmid && A)
        {
            if(rmid->val != A->val)
            {
                return false;
            }
            rmid = rmid->next;
            A = A->next;
        }
        return true;
    }
};

相交链表

题目链接:相交链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

 //方法一:暴力解法
 //时间复杂度:O(n^2)
 /*
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    struct ListNode* pcura = headA;
    while(pcura)
    {
        struct ListNode* pcurb = headB;
        while(pcurb)
        {
            if(pcura->val == pcurb->val)
            {
                if(pcura == pcurb)
                {
                    return pcura;
                }
            }
            pcurb = pcurb->next;
        }
        pcura = pcura->next;
    }
    return NULL;
}
*/

//方法二:双指针
//时间复杂度:O(m + n)
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    struct ListNode* pcura = headA;
    int counta = 0;
    while(pcura)//计算链表A的长度
    {
        counta++;
        pcura = pcura->next;
    }
    struct ListNode* pcurb = headB;
    int countb = 0;
    while(pcurb)//计算链表B的长度
    {
        countb++;
        pcurb = pcurb->next;
    }
    if(pcura != pcurb)//尾节点不相交代表不会相交
    {
        return NULL;
    }
    //pcura = headA;//重新指向头节点
    //pcurb = headB;
    //长的先走差距
    //假设法
    int gap = abs(counta - countb);
    struct ListNode* longList = headA;
    struct ListNode* shortList = headB;
    if(counta < countb)
    {
        longList = headB;
        shortList = headA;
    }
    while(gap--)
    {
        longList = longList->next;
    }
    while(longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
    return longList;
}

环形链表

题目链接:环形链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) 
{
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
        {
            return true;
        }
    }
    return false;
}

Q1:为什么一定会相遇,会不会错过,永远追不上?
A1:追及问题,两者之间的距离为n, n - 1, n - 2, …… , 3, 2, 1, 0。

Q2:slow一次走一步,fast一次走3步、4步、……、n步可以吗?
A2:假设N为slow进环时与fast的差距,C为带环部分长度

假设每次走3步

N为偶数N为奇数
NN
N - 2N - 2
N - 4N - 4
…………
43
21
0-1
  1. N是偶数,第一轮就追上了。
  2. N是奇数:
    • 如果C - 1是偶数,下一轮就追上了。
    • 如果C - 1是奇数,那么永远追不上。

真的是这样吗

假设slow进环时,fast跟slow距离是N

slow走距离是:L
fast走的距离:L+x*C+C-N

slow进环时,假设fast已经在环里面转了x圈

fast走的距离是slow的3倍
3 ∗ L = L + x ∗ C + C − N 3 * L = L + x * C + C - N 3L=L+xC+CN
2 ∗ L = ( x + 1 ) ∗ C − N 2 * L = (x + 1) * C - N 2L=(x+1)CN

偶数=(x+1)*偶数-奇数

N是奇数时,C也是奇数
N是偶数时,C也是偶数

同时存在N是奇数且C是偶数,那么就永远追不上

偶数=(x+1)*偶数-奇数

只有奇数-奇数才能等于偶数
(x+1)*偶数一定是偶数
所以反正出N是奇数且C的偶数不能同时存在,永远追不上的条件不成立

结论:一定能追上
N偶数第一轮就追上了
N是奇数第一轮追不上,C-1是偶数第二轮就追上

环形链表 II

题目链接:环形链表 II

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
        {
            struct ListNode* meet = slow;
            struct ListNode* phead = head;
            while(meet != phead)
            {
                meet = meet->next;
                phead = phead->next;
            }
            return meet;
        }
    }
    return NULL;
}

Q:为什么后面meet和phead走相同的距离
A:设从头到相交节点的距离为L,slow进环后走的距离为N(N不可能大于环的长度,自行论证),fast在环内转了x圈(N >= 1,自行论证),环的长度为C
slow和fast相遇时:
slow走的距离: L + N L + N L+N
fast走的距离: L + x ∗ C + N L + x * C + N L+xC+N
fast走的距离是slow走的距离的2倍: 2 ∗ ( L + N ) = L + x ∗ C + N 2 * ( L + N ) = L + x * C + N 2(L+N)=L+xC+N
得: L = x ∗ C − N L = x * C - N L=xCN
化简得: L = ( x − 1 ) ∗ C + C − N L = (x - 1) * C + C - N L=(x1)C+CN

随机链表的复制

题目链接:随机链表的复制

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */

struct Node* copyRandomList(struct Node* head) 
{
    struct Node* pcur = head;
    //拷贝节点插入到原节点的后面
    while(pcur)
    {
        struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
        newnode->val = pcur->val;
        newnode->next = pcur->next;
        pcur->next = newnode;
        pcur = newnode->next;
    }
    //拷贝random
    pcur = head;
    while(pcur)
    {
        struct Node* newnode = pcur->next;
        if(pcur->random == NULL)
        {
            newnode->random = NULL;
        }
        else
        {
            newnode->random = pcur->random->next;
        }
        pcur = newnode->next;
    }
    //取下拷贝的链表
    struct Node* newHead = NULL;
    struct Node* newTail = NULL;
    pcur = head;
    while(pcur)
    {
        struct Node* newnode = pcur->next;
        struct Node* next = newnode->next;
        if(newTail == NULL)
        {
            newHead = newTail = newnode;
        }
        else
        {
            newTail->next = newnode;
            newTail = newTail->next;
        }
        pcur->next = next;
        pcur = next;
    }
    return newHead;
}
  • 27
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 一本通 OJ 库的测试数据,通常是用来验证提交的代码在各种情况下的正确性。测试数据可以分为两种类型,手动和自动。 手动测试数据是由目的出人根据意和数据范围设计的一组数据,用来检测程序的正确性和运行效率。手动测试数据的优点是能够涵盖各种情况,但缺点是数量相对较少,不足以覆盖所有可能的情况。 自动测试数据是由程序自动生成的一组数据,可以生成大量的数据以检测程序的健壮性和效率。自动测试数据的优点是数量大且可以自动生成,但缺点是可能无法覆盖某些特殊情况,导致漏洞。 对于提交的代码,一本通 OJ 库会对其进行编译和运行,然后与测试数据进行比较,判断代码的正确性和效率。如果代码通过了测试数据,就会被判定为正确,否则会被判定为错误,并给出具体的错误信息,供用户进行调试和改进。 综上所述,一本通 OJ 库的测试数据是一个重要的组成部分,它可以帮助用户测试代码的正确性和运行效率,提高用户的编程技能,同时也可以帮助出人设计更好的目,并保证目的质量和难度。 ### 回答2: 一本通 oj库是一个在线的程序设计竞赛平台,提供了丰富的编程目和测试数据。测试数据是用于对程序进行测评的输入和输出数据集合。在目描述中,会对问进行详细的解释和要求,并提供多组测试数据作为样例,让程序员运行他们的代码,并得到程序的输出结果。 测试数据通常包括正向测试数据和反向测试数据。正向测试数据是指符合目条件的测试数据,覆盖了大多数情况,测试程序是否正确;而反向测试数据则是用于测试程序是否能够正确处理异常情况。 在使用一本通 oj库时,程序员不仅需要通过编写算法和程序的方式解决问,还需要通过分析测试数据来判断自己的代码是否正确。而一本通 oj库的丰富数据集合为程序员提供了充足的测试数据,帮助程序员准确地检测代码中存在的漏洞和错误。 总之,一本通 oj库提供了全面的测试数据来测试程序员的代码是否满足目描述和要求,是程序员进行程序设计竞赛、算法练习和编程学习的良好平台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值