文学的清流
但是太阳,他每时每刻都是夕阳也都是旭日。
当他熄灭着走下山
去收尽苍凉残照之际,
正是他在另一面燃烧着
爬上山巅布散烈烈朝辉之时。———《我与地坛》
一、添加逗号
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个结点
思路:
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;
}
进阶:使用快慢指针:
思路:
这里类似于物理当中的车距,两辆车之间始终保持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;
}
三、链表的回文结构
思路:
使后一半对称的结点都反转指向
注意:这里的结点个数可以为奇数和偶数
第一步:先找出中间结点。第二步:使后面的指针反转指向,如上图所示。第三步:从两端开始比较--相同,则向下一个结点进行比较。当比较到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;
}
四、相交链表
思路:
如果不相交,这两个链表最后一个结点的地址不同。相反地,这两个链表最后一个结点的地址都相同。这里主要探讨第二种情况。将这两个链表从最后面的结点开始对齐,然后将前面对齐的结点开始比较地址,直到有相交的地址。
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;
}
假设法:
五、随机链表的复制
思路:
这道题的难度在于random的指向,这样新旧结点连接,就可以通过旧结点的random的指向来控制新结点random的指向
这样就可以将每个新结点中random的指向与原链表相同,然后将新结点自成一个单链表,最后返回新链表的头结点。
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;
}