lintcode一刷习题
判断a与b哪个值大输出哪个
之前总是用if写两段,其实可以:
int m=(a>b?a:b);
三目运算,可以直接用int写定义一个变量,也可以在输出的时候直接写在printf里面。
printf("%d",a>b?a:b);
甚至可以使用宏定义,在代码开头进行宏定义:
#define MAX(a,b) (a>b)?a:b
使用时:
int max;
printf("x = %d, y = %d\n", x, y);
max = MAX(x, y);
printf("max = %d\n", max);
擅用while循环
用while(1)循环,在需要退出的地方写break。
printf输出
%d 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%p 指针的值
%e 指数形式的浮点数
%x, %X 无符号以十六进制表示的整数
%0 无符号以八进制表示的整数
%g 自动选择合适的表示法
在%d前加数字可以表示输出的字符位数,%-8d 表示为 左对齐的8位字符的整数。%8d表示为右对齐的8位字符整数。
- 对于变量
x
、y
、z
,使用 右对齐,占用 10 个字符宽度,保留 6 位小数精度,输出有符号的双精度浮点数printf("%10.6lf,%10.6lf,%10.6lf\n",x,y,z);
- 对于变量
i
、j
、k
,使用 右对齐,占用 4 个字符宽度,输出十进制有符号的短整型printf("%4hd,%4hd,%4hd\n",i,j,k);
scanf输入
- 读取第一行数据中的 第三个数字
scanf("%*d %*d %d", &num);
%*d 表示忽略掉现在输入的这个数字,所以要读取第三个数字,即忽略掉前两个。
使用 scanf() 清空缓冲区
scanf("%*[^\n]");
scanf("%*c");
第一个 scanf()
将逐个读取缓冲区中 \n
之前的其他字符,%
符号后面的 *
表示将读取的这些字符丢弃,遇到 \n
字符时便停止读取。此时,缓冲区中尚有一个 \n
遗留。
第二个 scanf()
再将这个 \n
读取并丢弃,这里的星号和第一个 scanf()
的星号作用相同。由于所有从键盘的输入都是以回车结束的,而回车会产生一个 \n
字符,所以将 \n
连同它之前的字符全部读取并丢弃之后,也就相当于清除了输入缓冲区。
输入一维数组
scanf("%[^\n]",str);
char str1[100],str2[100];
scanf("%s%s",str1,str2);
定义两个字符数组char,可以直接用数组名当作指针,直接键入数组内容。
输入二维数组
int str[3][3];
int i,j,a=0;
for(i=0;i<3;i++){
for(j=0;j<3;j++)
{
scanf("%d",&str[i][j]);
}
}
家人们 ,2/22 把力扣上的c语言的习题写完了,明天开始准备跟着力扣c语言的知识点再过一遍。我好菜啊,加油吧家人们TAT
3/2开坑随想录了家人们 冲!
随想录
1.两数之和
(int*)malloc(sizeof(int));
语句给指针变量分配一个整型存储空间。
在代码中定义一个整型数组,可以直接利用这个代码:
int* ret = malloc(sizeof(int) * 2)
表示定义一个ret数组指针,内存大小为两个整型int变量。
2.移除元素
如何在不新增数组的情况下去除元素生成新数组。设置双指针,将快指针搜索到的内容赋值给慢指针,覆盖之前数组内容。
int fast=0 ,slow=0;
while(fast<numsSize){
if(nums[fast]!=val){ //让快指针去匹配
nums[slow]=nums[fast]; //匹配到传递给慢指针
fast++;
}
slow++;
}
return slow;
3.长度最小的子数组(滑动窗口)
输出数组中满足条件的子数组,采用滑动窗口算法
算法思路:
(1)数组右指针右移,让数组从头开始做加法,加至满足条件后,进入while循环;
(2)记录当前子数组长度,并将左指针右移,判断条件是否满足,不满足右指针继续右移;若满足,更新当前子数组长度;
int sublength = right - left + 1; // 更新数组长度
min = min < sublength ? min : sublength; // 输出最小的长度
重定义子数组长度,再与上一次数组长度进行对比;
(3)直到右指针全部遍历完整个数组,退出for循环,最后输出子数组长度。
完整代码:
int minSubArrayLen(int target, int* nums, int numsSize) {
int min = INT_MAX; // 取最大值以方便后续不断更新INT_MAX为2147483647 ;
int sum = 0;
int left = 0, right = 0;
for (; right < numsSize; ++right) {
sum += nums[right]; // 右加加至等于或大于target
while (sum >= target) {
int sublength = right - left + 1; // 更新数组长度
min = min < sublength ? min : sublength; // 输出最小的长度
sum -= nums[left++]; // 减去左边边界
}
}
return min == INT_MAX ? 0 : min; // 返回最小长度
}
4.移除链表元素
啊啊啊啊啊啊两天速成一个链表,从来没学过链表这东西,一看原来是数据结构的,我根本没学过这门课。。亏我昨天坐火车还在看链表,终于给我看懂了一点。
typedef struct ListNode ListNode;
(1)宏定义struct ListNode 为ListNode(方便后续)创建一个节点;
(2)创建一个虚拟头节点,将其创建在头节点前面;
typedef struct ListNode ListNode;
ListNode *shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
(3)创建一个临时指针节点cur,从虚拟头节点开始;
ListNode*cur = shead;
(4)如果节点不为空且节点的下一个值为所找的值,则删除节点;
while(cur->next!=NULL){
if(cur->next->val==val){
ListNode*tmp=cur->next;
cur->next=cur->next->next;
free(tmp);
}
(5)如果不是,则cur继续遍历;
else {
cur = cur->next;
}
(6)将虚拟头节点返回给头节点head;并删除虚拟头节点,最后返回头节点。
head = shead ->next;
free(shead);
return head;
5.创建链表
(1)
MyLinkedList()
初始化MyLinkedList
对象。typedef struct MyLinkedList{ int val; struct MyLinkedList* next; } MyLinkedList; //创建链表 MyLinkedList* myLinkedListCreate() { //定义头指针 MyLinkedList *head=(MyLinkedList*)malloc(sizeof(MyLinkedList)); head->next=NULL; return head; }
int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。搜索目标节点-
int myLinkedListGet(MyLinkedList* obj, int index) { MyLinkedList*cur = obj ->next;//创建临时指针cur遍历链表 for(int i=0;cur!=NULL;i++){ if(i==index){ //找到index return cur->val; //返回此时cur的值val } else{ cur = cur->next; //继续遍历 } } return -1; //找不到返回-1 }
void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。创建头节点void myLinkedListAddAtHead(MyLinkedList* obj, int val) { MyLinkedList *shead=(MyLinkedList*)malloc(sizeof(MyLinkedList)); shead->val=val; shead->next=obj->next;//把当前节点的下一环给虚拟头节点连上(牵右手 obj->next=shead;//把当前节点连到虚拟头节点上(牵左手 }
void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。创建尾节点
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList*cur=obj;
while(cur->next!=NULL){ //不是尾节点就继续遍历
cur=cur->next;
}
MyLinkedList*stail=(MyLinkedList*)malloc(sizeof(MyLinkedList));
stail->val=val;
stail->next=NULL; //遍历到尾节点
cur->next=stail; //牵左手
}
void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。插入元素void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) { if(index==0){ myLinkedListAddAtHead(obj,val);//创建头节点 return; } MyLinkedList*cur=obj->next; //创建临时指针cur从头遍历 for(int i==0;cur!=NULL;i++){ if(i==index){ //遍历到该值时 MyLinkedList*new=(MyLinkedList*)malloc(sizeof(MyLinkedList)); new->val=val; new->next=cur->next;//牵左手 cur->next=new;//牵右手 return; } else{ cur=cur->next; } } }
void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。删除节点void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) { if (index == 0){ MyLinkedList *tmp = obj->next; if (tmp != NULL){ obj->next = tmp->next; free(tmp); } return; } MyLinkedList *cur = obj->next; for (int i = 1 ;cur != NULL && cur->next != NULL; i++){ if (i == index){ MyLinkedList *tmp = cur->next; if (tmp != NULL) { cur->next = tmp->next;//跨过tmp连接 free(tmp);//删除tmp } return; } else{ cur = cur->next; } } }
-
综上,当所设置的指针需要赋值时,即需要分配内存空间,设定时需要malloc;若只是临时指针cur,只是用来遍历列表的,不需要分配内存,可以直接MyLinkedList *tmp=cur->next。
-
插入节点:用i遍历至index后;new->val=val;
new->next=cur->next;//牵左手
cur->next=new;//牵右手 -
删除节点:用i遍历至index后 cur->next = tmp->next;//跨过tmp连接
free(tmp);//删除tmp
6.反转链表
反转链表即把链表的每一步的指向都进行反转,定义一个临时指针cur以及头节点前的指针pre,包括链表自带的头节点head,共三个节点。
cur为临时指针,不需要赋值,pre为头节点的前面的指针,需要反向指向NULL。
接下来进行节点移动+反转:首先将cur定位至头节点的下一个节点处(防止头节点反向之后无法定位到下一个);再将头节点进行反转,即将头节点的next连接到pre;接下来移动pre指针至头节点处,再将头节点移动至cur处(防止先移动头节点,pre没有办法定位移动)。整个操作包围至while循环中,条件为head不为NULL,因为为NULL时表示节点已经全部移动完成。
完整代码:
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* cur; //定义临时指针
struct ListNode* pre = NULL; //定义头节点之前的指针
while(head){
cur=head->next; //先移动cur用于标记头节点下一个的位置
head->next=pre;//反转指向,将头节点指向前面的pre
pre=head;//先将pre进行往前移动 防止移动head失去该结点定位
head=cur;//再移动head
}
return pre;//最后pre移动到头节点,头节点到null,所以返回pre
}
7.两两交换链表中的节点
要求交换节点并不是交换数值,所以要对节点进行交换,只需要将节点的指向进行交换。由图可知交换后的链表由数字2开始,指向数字1。所以我们先设置一个虚拟头节点(个人认为在需要更改和处理头节点指针时都需要设置一个虚拟头节点)
(1)设置虚拟头节点shead,以及定义一个临时指针cur,将其等价于shead;
(2)更改cur的指向,很明显现在的2为cur的后两个节点,要将其改为后一个,所以:cur->next=cur->next->next;
(3)此时1位置的标记已经断开,无法定位1,所以我们在开始还需要设置一个临时定位指针tmp,将其定义为tmp=cur->next;之后可以定位至1,将2的next连到1:cur->next->next=tmp;
(4)下一步将链表链接至3,此时由于2的next已经断开,无法定位至3,所以在开始我们也要设置临时定位节点指向3,即tmp1=cur->next->next->next;之后将1的后续链接至3: cur->next->next->next=tmp1;
(5)接下来对3和4进行操作,我们的cur就应该指向3的前一个节点1;所以cur=cur->next->next;
(6)最后返回的是虚拟头节点的后一位,才是完整链表,即:return shead->next;
完整代码:
struct ListNode* swapPairs(struct ListNode* head) {
typedef struct ListNode ListNode;
ListNode *shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
ListNode*cur=shead;
while(cur->next!=NULL&&cur->next->next!=NULL){
ListNode*tmp=cur->next;
ListNode*tmp1=cur->next->next->next;
cur->next=cur->next->next;
cur->next->next=tmp;
cur->next->next->next=tmp1;
cur=cur->next->next;
}
return shead->next;
}
8.有效的字母异位词
开哈希表了家人们,依旧是根本没学过,太菜了。。。。
将字符串1的各字母的ASCII值记录在哈希表中,再将字符串2中各字母ASCII值从哈希表中减去,如果恰好减完位为0,证明是字母异位词,如果不是0,则说明不是。
完整代码:
bool isAnagram(char* s, char* t) {
int record[26]={0}; //创建哈希表
int len_s =strlen(s),len_t=strlen(t); //计算输入的两个字符串的字长
for(int i=0;i<len_s;i++){ //统计字符串1的ASCII差值
record[s[i]-'a']++;
}
for(int i=0;i<len_t;i++){ //减去字符串2的ASCII值
record[t[i]-'a']--;
}
for(int i=0;i<26;i++){ //判断是否为0
if(record[i]!=0){
return false;
}
}
return true;
}
9.两个数组的交集
定义两个数组,一个用来存储数据出现次数的数组nums1cnt(计数),一个用来存储数据的数组result(结果)。
计数数组用普通数组定义即可:int nums1cnt[1000]={0};
存储数据的数组需要用指针定义:int *result=(int*)malloc(sizeof(int));
总体思路为:先遍历数组1,将出现的数字次数存储在nums1cnt中, nums1cnt[nums1[i]]++; 将nums1cnt中下标为nums1的数字次数进行+1;
再将num2数组放入nums1cnt中,判断是否存在>0;即出现相同数字;
如果存在,就将该数字存入结果数组中result;
完整代码:
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
int nums1cnt[1000]={0};//定义计数数组
int lesssize=nums1Size<nums2Size?nums1Size:nums2Size;
int*result =(int*)malloc(sizeof(int));//定义结果数组
int resultindex=0;
int* tempnums;
int i;
for(i=0;i<nums1Size;i++){
nums1cnt[nums1[i]]++;//计num1数组次数
}
for(i=0;i<nums2Size;i++){
if(nums1cnt[nums2[i]]>0){//判断num2数组重复次数
result[resultindex]=nums2[i];//计入结果数组
resultindex++;
nums1cnt[nums2[i]]=0;
}
}
*returnSize=resultindex;
return result;
}
10.反转字符串中的单词
point:怎么不反转单词内容的情况下对整个顺序进行调换?难道能把一个单词变成一个组合再进行顺序调换?答案肯定是否定的。
解题思路:将整个字符串进行反转,后根据空格区分单个字母,再将字母内容进行反转。
(1)先遍历整个字符串删除多余的空格,遍历到第一个非空字符,定义一个新的数组下标slow,把遍历到的字符串赋值给新下标slow,生成一个新的字符串:
void removeExtraSpace(char* s) {
int start = 0; // 指向字符串开头的指针
int end = strlen(s) - 1; // 指向字符串结尾的指针
while (s[start] == ' ') start++; // 移动指针 start,直到找到第一个非空格字符
while (s[end] == ' ') end--; // 移动指针 end,直到找到第一个非空格字符
int slow = 0; // 指向新字符串的下一个写入位置的指针
for (int i = start; i <= end; i++) { // 遍历整个字符串
if (s[i] == ' ' && s[i+1] == ' ') { // 如果当前字符是空格,并且下一个字符也是空格,则跳过
continue;
}
s[slow] = s[i]; // 否则,将当前字符复制到新字符串的 slow 位置
slow++; // 将 slow 指针向后移动
}
s[slow] = '\0'; // 在新字符串的末尾添加一个空字符
}
(2)反转字符串(在指定区域)