一起刷刷题咯

文学的清流

但是太阳,他每时每刻都是夕阳也都是旭日。

当他熄灭着走下山

去收尽苍凉残照之际,

正是他在另一面燃烧着

爬上山巅布散烈烈朝辉之时。———《我与地坛》

一、添加逗号

ACM编程 添加逗号

技能 JS基础语法 Go基础语法 Python基础 Java基础语法 C++基础语法 知识点 签到

时间限制:1s 空间限制:256MB

限定语言:C(clang11), C++(clang++11), Pascal(fpc 3.0.2), Java(javac 1.8), Python2(2.7.3),                  PHP(7.4.7), C#(mcs 5.4), ObjC(gcc 5.4), Python3(3.9), JavaScript Node(12.18.2),                 JavaScript V8(6.0.0), Sqlite(3.7.9), R(4.0.3), Go(1.14.4), Ruby(2.7.1), Swift(5.3),                 matlab(Octave 5.2), pypy2(pypy2.7.13), pypy3(pypy3.6.1), Rust(1.44), Scala(2.11.12),                 Kotlin(1.4.10), Groovy(3.0.6), TypeScript(4.1.2), Mysql(8.0)

题目描述:对于一个较大的整数 N(1<=N<=2,000,000,000) 比如 980364535,我们常常需要一位一位数这个数字是几位数,但是如果在这 个数字每三位加一个逗号,它会变得更加易于朗读。 因此,这个数字加上逗号成如下的模样:980,364,535请写一个程序帮她完成这件事情

输入描述:一行一个整数 N

输出描述:一行一个字符串表示添加完逗号的结果 补充说明:1≤n≤2,000,000,000

题解一:暴力解法:

思路:我先假设输入的数字是12345678,要打印12,345,678。首先根据它的位数来得出mul=1000 0000(先不说明mul),这里用一个标志flag(它是指在最前面要打印的两位数12)来说明是否打印了12。打印12时,我先用Mul(flag)求出100,然后n%(mul/100)的出来的数是12。此时将flag=0,就不会再打印12了,打印12后就将mul/=100。其次通过这个表达式n / mul % 1000,先将它拆分成n/mul和%1000,n/mul=12345,然后再%1000就会打印出345,将mul/=1000,得mul=1000。然后再次上述步骤就会打印678,此时mul已经为0了,就说明后面已经没有要打印的数字,然后不打印逗号,跳出循环。

#include<stdio.h>
int Count(long long n)//记录有几位数
{
    int count = 0;
    while (n > 0)
    {
        n /= 10;
        count++;
    }
    return count;
}
long long Mul(int count)//Mul是用来求10的几次方
{
    long long mul = 1;
    while (count--)
    {
        mul *= 10;
    }
    return mul;
}
int  main()
{
    long long n=0,mul=0;
    int count, single;
    scanf("%lld", &n);
    count = Count(n);
    mul = Mul(count);
    flag = count % 3;
    while (mul>1)
    {
        if (flag > 0)//这里是用来打印最前面的位数比如12345678,这里打印12
        {
            mul = mul / Mul(flag);
            printf("%lld", n / mul);//如果这个数的位数不是三的倍数,那就先打印最前面一位或者二位
            flag = 0;
        }
        else
        {
            if (mul > 1)
            {
                mul /= 1000;
                printf("%lld", n / mul % 1000);
            }
        }
        if (mul > 1)
            printf(",");
    }
    return 0;

题解二:递归

思路:递归思想是传递与回归。那这里传递啥呢,回归啥呢。把这个思路缕清就知道该怎么写递归了。这里还是先假设输入的数字是12345678,打印12,345,678。那么就要先打印12,这里就可以想到12345678变成12345再变成12,传递就这样想好了,那什么时候回归呢,肯定是传递着的值为0时回归。这样就可以打印出12345678,那么如何加上逗号呢?我这里用一个标记,flag。当函数第一次被调用是,它完成的任务是打印678,而接下来每次的打印都要加上逗号,所以函数第一次被调用时,flag=0,就不会打印逗号了。然后第二次函数调用,flag就会被赋给1,打印逗号。刚刚好没有打印的数字时就退出。

#include<stdio.h>
void Pri(long long n,int count)
{
    if (n==0)
        return;
    if (n)
    {
        Pri(n / 1000,count+1);//特别要注意:这里函数调用++count不能写成count++,
        printf("%lld", n % 1000);
        if (count>0) //因为最后面的三个数是最后打印,而且不带逗号,这里作出一个标志,根据递归的特点,让它初始值为0,第一次调用时,它打印的是最后三位数,刚刚好这里flag为0
        {//当再次调用时,flag就不为0,就会打印逗号
            putchar(',');
        }
    }
    return;
}
int main()
{
    long long n;
    int count=0;
    scanf("%lld", &n);

    Pri(n,count);
    return 0;
}

 

二、删除链表的倒数第N个结点b3a1c8e200384f8293696524f7a39b54.png

思路:

1.先记录它有多少个结点  2.再用两个指针指向要删除的结点和前一个结点  3.将前一个结点只向要删除结点的后一个结点(这里还有特殊情况要分析:只有一个结点和删除第一个结点)

 typedef struct ListNode LN;

struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {

    LN*des=head;

    LN*destroy=NULL;

    int count=0;

    while(des!=NULL)

    {

        count++;

        des=des->next;

    }

    for(des=head;count>n+1;count--)

    {

        des=des->next;//des比count提前一个

    }

    if(count==n)       //特殊情况:这就好比单链表中的头删,将在删除第一个结点之前,就让头指针指向第二个结点(如果只有一个结点,头指针也能只向空)

    {

        destroy=des;

        des=NULL;

        head=head->next;

        free(destroy);

    }

    else          //逻辑思路:先将要删除的结点两边的结点相连,再释放要删除的空间

    {

    destroy=des->next;

    des->next=destroy->next;

    free(destroy);

    destroy=NULL;

    }

    return head;

}

进阶:使用快慢指针:

05579091d8164af980ef362810674924.png

思路:

这里类似于物理当中的车距,两辆车之间始终保持K米。相同地,先让快指针走到第k个结点,然后让快慢指针一起走。当快指针指向的下一个是NULL,此时,慢指针是倒数第K个结点。

typedef struct ListNode LN;

int kthToLast(struct ListNode* head, int k){

    LN*first,*second;

    second=first=head;

    while(--k)

    {

        first=first->next;

    }

    while(first->next)

    {

        first=first->next;

        second=second->next;

    }

return second->val;

}

三、链表的回文结构

0111537cc9624fd1ab448ed526a5a46f.png

思路:

使后一半对称的结点都反转指向

1ab9ab9cf66e4608aa0b612a4bbac86f.png

注意:这里的结点个数可以为奇数和偶数

第一步:先找出中间结点。第二步:使后面的指针反转指向,如上图所示。第三步:从两端开始比较--相同,则向下一个结点进行比较。当比较到3时,这里有很明显的指向关系,根据指向的地址来确认是该链表是回文结构。

LN*fast,*slow,*mid;

    fast=slow=A;

    while(fast&&fast->next)//先找到中间结点

    {

        slow=slow->next;

        fast=fast->next->next;

    }

    //根据算法逻辑,这里不用管链表结点个数是奇数或偶数

    mid=slow->next;//使后一半对称的结点都反转指向

    fast=mid->next;

    while(fast)

    {

        mid->next=slow;//使中间结点指向slow

        slow=mid;

        mid=fast;

        fast=fast->next;

    }//这里走完,最后一个结点还没指向倒数第二个,而fast已经为空了

    mid->next=slow;

    fast=mid;//指向最后一个结点

    slow=A;//slow指向头结点

    while(fast->val==slow->val) //开始比较

    {

        if(slow==fast||slow->next==fast)//这里要说明的是:假设1 2 2 1结点,第一个2指向第二个2的地址

        {

            return true;

        }

        fast=fast->next;

        slow=slow->next;

    }

    return false;

    }

四、相交链表

思路:

如果不相交,这两个链表最后一个结点的地址不同。相反地,这两个链表最后一个结点的地址都相同。这里主要探讨第二种情况。将这两个链表从最后面的结点开始对齐,然后将前面对齐的结点开始比较地址,直到有相交的地址。

4ddbb643b7e6425891c3c927b25a36ba.png

 typedef struct ListNode LN;

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {

    int l1size,l2size;     //用来记录两个链表的长度

    LN*l1,*l2;     //建立两个指针分别指向两个链表

    l1=headA;

    l2=headB;

    l1size=l2size=1;

    while(l1->next)

    {

        l1=l1->next;

        l1size++;

    }//计算l1、l2链表长度

    while(l2->next)

    {

        l2=l2->next;

        l2size++;

    }

    if(l2!=l1)//这里判断它们是否有相交结点,如果没有,则返回空指针

    {

        return NULL;

    }

    l1=headA;

    l2=headB;

    if(l1size>l2size)   //这里也可以使用假设法,将长链表和短链表赋给新指针

    {

        l1size-=l2size;

        while(l1size--)//使得两个指针指向的位置对等

        {

            l1=l1->next;

        }

    }

    else

    {

        l2size-=l1size;

        while(l2size--)

        {

            l2=l2->next;

        }

    }

     while(l1!=l2)//当l1,l2指向相同的地址时,退出

        {

            l1=l1->next;

            l2=l2->next;

        }

    return l1;

}

假设法:

cf0b611784b1472ab6f05c31f7ef57a7.png

五、随机链表的复制

思路:

这道题的难度在于random的指向,这样新旧结点连接,就可以通过旧结点的random的指向来控制新结点random的指向

66d13a266e304d74a783a231ad18c0c6.png

这样就可以将每个新结点中random的指向与原链表相同,然后将新结点自成一个单链表,最后返回新链表的头结点。

87f1209a6e704d0bb67988a2379953eb.png

typedef struct Node Node;

struct Node* copyRandomList(struct Node* head) {

    if(head==NULL)

    {

        return NULL;

    }

    Node*tail=head;

    Node*newnode=NULL;

    while(tail)//先在每个结点后面建立它的新结点

    {

        newnode=(Node*)malloc(sizeof(Node));

        newnode->next=tail->next;

        newnode->val=tail->val;

        tail->next=newnode;//使自己指向复制的自己

        tail=newnode->next;

    }

    tail=head;

    newnode=tail->next;

    while(tail)//这里的循环判断没有起到作用

    {

        if(tail->random!=NULL)

        {

            newnode->random=tail->random->next;//使新结点random的指向和旧结点random指向相同的复制的结点

        }

        else

        {

            newnode->random=NULL;

        }

        tail=newnode->next;

        if(tail==NULL)

            break;

        newnode=tail->next;

    }

    tail=head;

    newnode=tail->next;

    Node*newhead=newnode;//指向新的头结点

    while(newnode->next)//连接各个新结点

    {

        tail->next=newnode->next;

        tail=tail->next;

        newnode->next=tail->next;

        newnode=newnode->next;

    }

    return newhead;

}

 

评论 50
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值