目录
4、实现函数memcpy(), strcpy(), strcmp(), strcat()
6、设计函数 int atoi(char *s),void itoa(int n, char s[])
1、将字符串反转
// 实现字符串反转的两个函数
// 第一个函数,参数为字符数组
void reserverString(char str[])
{
char tmp; // 定义一个临时变量
int i, j; // 定义两个整型变量
// 循环遍历字符数组,i从0开始,j从字符串长度减1开始,i小于j时循环
for (i = 0, j = strlen(str) - 1; i < j; i++, j--)
{
tmp = str[i]; // 将str[i]的值赋给tmp
str[i] = str[j]; // 将str[j]的值赋给str[i]
str[j] = tmp; // 将tmp的值赋给str[j]
}
}
// 第二个函数,参数为字符指针
char* reserverString1(char* str)
{
char* start = str; // 定义一个字符指针start,指向字符串的首地址
char* end = str + strlen(str) - 1; // 定义一个字符指针end,指向字符串的末尾地址
char tmp; // 定义一个临时变量
// 如果传入的字符串不为空
if (NULL != str)
{
// do-while循环,先执行一次循环体,再判断条件是否成立
do
{
tmp = *start; // 将start指向的字符赋给tmp
*start++ = *end; // 将end指向的字符赋给start指向的字符,然后start指针向后移动一位
*end-- = tmp; // 将tmp的值赋给end指向的字符,然后end指针向前移动一位
} while (start<end); // 当start指针小于end指针时继续循环
}
return str; // 返回反转后的字符串
}
2、数字翻转和回文判断
数字翻转
int reverse(int num) {
int reversed = 0;
while (num != 0) {
reversed = reversed * 10 + num % 10;
num /= 10;
}
return reversed;
}
#include <stdio.h>
int main() {
int num, reversed_num = 0, remainder, original_num; // 定义整型变量num、reversed_num、remainder和original_num
printf("Enter an integer: "); // 输出提示信息,要求用户输入一个整数
scanf("%d", &num); // 读取用户输入的整数,并存储到变量num中
original_num = num; // 将变量num的值赋给变量original_num
while (num != 0) { // 当变量num不等于0时,执行循环体内的语句
remainder = num % 10; // 取变量num除以10的余数,存储到变量remainder中
reversed_num = reversed_num * 10 + remainder; // 将变量reversed_num乘以10再加上变量remainder的值,存储到变量reversed_num中
num /= 10; // 将变量num除以10的结果,存储到变量num中
}
if (original_num == reversed_num) { // 如果变量original_num等于变量reversed_num,执行if语句块内的语句
printf("%d is a palindrome.\n", original_num); // 输出变量original_num是回文数的信息
} else { // 否则,执行else语句块内的语句
printf("%d is not a palindrome.\n", original_num); // 输出变量original_num不是回文数的信息
}
return 0; // 返回0,表示程序正常结束
}
3、大小端问题
(1)判断大小端
方法一:输入一个字符变量,若数据的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处,则是大端存储方式;若数据的低位字节存储在内存的低地址处,高位字节存储在内存的高地址处,则是小端存储方式。
#include <stdio.h>
#include <stdlib.h>
int check_sys()
{
int a = 1;
return *(char*)&a;//返回1表示小端,返回0表示大端
}
int main()
{
if (check_sys() == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
system("pause");
return 0;
}
方法二:用联合体
(2)大小端转换
//对于16位数据,定义一个宏BIG2LITTLE,用于将大端字节序转换为小端字节序
#define BIG2LITTLE(A) ((((A)&0xff00)>>8) | (((A)&0x00ff)<<8))
//对于32位数据,定义一个宏BIG2LITTLE,用于将大端字节序转换为小端字节序
#define BIG2LITTLE(A) (((A)&0xff000000)>>24) | (((A)&0x000000ff)<<24) | (((A)&0x0000ff00)<<8) | (((A)&0x00ff00)>>8))
//1. 宏定义:#define 宏名(参数) 宏体
//2. & 位运算符:按位与,将两个数的二进制位进行与运算,只有两个数对应位都为1时,结果才为1,否则为0
//3. | 位运算符:按位或,将两个数的二进制位进行或运算,只要两个数对应位有一个为1,结果就为1,否则为0
//4. >> 位运算符:右移运算符,将一个数的二进制位向右移动指定的位数,移动后高位补0
//5. << 位运算符:左移运算符,将一个数的二进制位向左移动指定的位数,移动后低位补0
//6. &0xff00:将A的高8位清零,低8位保留
//7. &0x00ff:将A的低8位清零,高8位保留
//8. ((((A)&0xff00)>>8) | (((A)&0x00ff)<<8)):将A的高8位和低8位交换位置,得到小端字节序
//9. &0xff000000:将A的高8位清零,低24位保留
//10. &0x000000ff:将A的低24位清零,高8位保留
//11. &0x0000ff00:将A的低16位和高8位清零,中间的8位保留
//12. ((((A)&0xff000000)>>24) | (((A)&0x000000ff)<<24) | ((A)&0x0000ff00)<<8) | (((A)&0x00ff00)>>8)):将A的高24位和低8位交换位置,将A的次高8位和次低8位交换位置,得到小端字节序。
4、实现函数memcpy(), strcpy(), strcmp(), strcat()
(1)函数原型 :void *memcpy(void *dest, const void *src, size_t n);
功能:从源src所指的内存地址的起始位置开始,拷贝n个字节到目标dest所指的内存地址的起始位置中,函数返回dest的值。
(2)函数原型 :void strcpy (char *dest,const char*src);
功能:将src拷贝到dest中
char* strcpy(char* dest, const char* src) {
char* p = dest;
while (*src != '\0') {
*p++ = *src++;
}
*p = '\0';
return dest;
}
(3)函数原型 :int strcmp(const char *s,const char * t);
功能:比较字符串s和t,并且根据s按照字典顺序小于,等于或大于t的结果分别返回负整数,0,正整数。
int strcmp(const char *s, const char * t)
{
for (; *s == *t; s++, t++)//注意for循环,能循环下去的条件的两者元素对应相等,一旦不相等立即退出
if (*s == '\0')
return 0;//表示相等
return *s - *t;
}
(4)函数原型 :char *strcat(char *dest,char *src);
功能:把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0',返回指向dest的指针
char *strcat(char *dst, const char *src) {
// 找到目标字符串的末尾
char *p = dst + strlen(dst);
// 逐个字符追加源字符串,直到遇到 null 字符
while (*src != '\0') {
*p++ = *src++;
}
// 添加 null 字符
*p = '\0';
return dst;
}
(5)函数原型 :void *memmove(void *dest, const void *src, size_t n);
功能:将src指向的内存块中的n个字节拷贝到dest指向的内存块中。与memcpy()函数不同的是,memmove()函数可以处理源内存块和目标内存块重叠的情况,保证拷贝的正确性。因此,memmove()函数更加安全,但是相对于memcpy()函数来说,它的执行效率可能会稍微低一些。
void *my_memmove(void *dest, const void *src, size_t count)
{
char *tmp; //定义一个char类型的指针tmp
const char *s; //定义一个const char类型的指针s
if((dest + count < src) || (src+ count) < dest)){ //如果两个内存没有重叠
tmp = dest; //将目标内存的地址赋值给tmp
s = src; //将源内存的地址赋值给s
while (count--) //循环count次
*tmp++ = *s++; //将源内存中的数据复制到目标内存中
} else { //如果两个内存有重叠
tmp = dest; //将目标内存的地址赋值给tmp
tmp += count; //将tmp指针移动到目标内存的末尾
s = src; //将源内存的地址赋值给s
s += count; //将s指针移动到源内存的末尾
while (count--) //循环count次
*--tmp = *--s; //将源内存中的数据复制到目标内存中
}
return dest; //返回目标内存的地址
}
5、实现最简单的hello world字符设备驱动
//1、头文件的包含
#include <linux/init.h>
#include <linux/module.h>
//许可的声明
MODULE_LICENSE("Dual BSD/GPL");
//3、函数注意static、返回值、printk、KERN
static int hello_init(void)
{
printk(KERN_ALERT "Hello World!\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
//4、注意这两个宏函数的使用
module_init(hello_init);
module_exit(hello_exit);
6、设计函数 int atoi(char *s),void itoa(int n, char s[])
(1)int atoi(char *s)
描述:把字符串转换成整型数的一个函数。int atoi(const char *nptr) 函数会扫描参数 nptr字符串,跳过前面的空白字符(例如空格,tab缩进)等,可以通过isspace( )函数来检测),直到遇上数字或正负符号才开始做转换,而在遇到非数字或字符串结束符('\0')才结束转换,并将结果返回。如果 nptr不能转换成 int 或者 nptr为空字符串,那么将返回0。特别注意,该函数要求被转换的字符串是按十进制数理解的。
int atoi(char s[])
{
int i, n, sign;
for (i = 0; isspace(s[i]); i++); /*跳过空白符:空格时会一直循环,直到遇到不是空格*/
sign = (s[i] == '-') ? -1 : 1;//遇到的如果不是-号,比如是数字,则说明不是负数
if (s[i] == '+' || s[i] == '-') /* 跳过符号 */
i++;
for (n = 0; isdigit(s[i]); i++)
n = 10 * n + (s[i] - '0');
return sign * n;
}//注意判断空格、是否是数字的函数,你懂写吗?
int atoi(char *s) {
int num = 0;
int sign = 1;
if (*s == '-') {
sign = -1;
s++;
}
while (*s != '\0') {
if (*s >= '0' && *s <= '9') {
num = num * 10 + (*s - '0');
} else {
break;
}
s++;
}
return num * sign;
}
(2)void itoa(int n, char s[])
功能:将整型数字转换为字符串
void itoa(int n, char s[])
{
int i, sign;
if ((sign = n) < 0) //记录符号
n = -n; //使n成为正数
i = 0;
do
{ //以反序生成数字 //假设n=987654
s[i++] = n % 10 + '0'; //n % 10 + '0'表示数字对应的字符,比如第一次的数字是4,变成字符4,是4+'0'
} while ((n /= 10) > 0); //此时n变成了98765
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
7、链表操作(单链表的插入、删除节点、遍历、反转)
8、排序算法
9、二分查找
/*
参数:数组,数组元素个数,要查找的数
返回:成功找到返回元素下标;失败返回-1
*/
int binary_search(int a[], int len, int num)
{
// 定义查找范围的下限和上限
int low = 0, high = len-1;
// 定义中间位置
int mid = (low+high) / 2;
// 当下限小于等于上限时,继续查找
while(low <= high){
// 如果中间位置的元素等于要查找的数,返回中间位置下标
if(num == a[mid]){
return mid;
}
// 如果中间位置的元素小于要查找的数,将下限移动到中间位置的右侧,继续查找
else if(num > a[mid]){
low = mid + 1;
mid = (low+high) / 2;
}
// 如果中间位置的元素大于要查找的数,将上限移动到中间位置的左侧,继续查找
else{
high = mid - 1;
mid = (low+high) / 2;
}
}
// 如果查找失败,返回-1
return -1;
}
10、 字符串转换数字
int stoi(char *p)
{
int i = 0;
for(i = 0; *p; p++){
i = (*p - '0') + 10 * i;
}
return i;
}
11、求一个数中1的位数
算法:让一个数的最低位和1位与,位与完数向右移1位,如果与的结果位1,计数+1。返回计数值。
// 统计一个整数二进制表示中1的个数
int NumOne(int i)
{
int ret = 0; // 初始化计数器为0
while(i){ // 当i不为0时,继续循环
if(i & 1) // 如果i的二进制表示的最后一位是1
ret++; // 计数器加1
i = i>>1; // 将i的二进制表示向右移动一位,相当于除以2
}
return ret; // 返回计数器的值
}
12、判断一个数是否为2的幂
算法:2的次幂的二进制特征是最高位为1,其余全0。可以用它和(它-1)位与,结果为0表示是,反之不是。
/*
传一个整数,当这个数为2的幂时返回0;不是返回1
*/
int judge(int i)
{
if(i <= 0) // 判断是否为正数
return 1; // 不是正数,返回1
else{
if(!(i & (i-1))) // 判断是否为2的幂
return 0; // 是2的幂,返回0
else
return 1; // 不是2的幂,返回1
}
}
13、实现atoi
描述:字符串转换成数字,前面跳过空格,当第一个字符为+、-、数字字符时,函数转换,直到非数字字符,返回转换后的整型数。若非空格后的第一个字符不是上面三者其一,则返回0。
int my_atoi(const char s[])
{
int i;
int sign;
int ret = 0;
for(i = 0; isspace(s[i]); i++); // 从字符串开头开始跳过空格,isspace()函数用于判断字符是否为空格
sign = (s[i] == '-') ? -1 : 1; // 判断符号,如果第一个非空格字符是'-',则为负数,否则为正数
if(s[i] == '+' || s[i] == '-'){ // 如果第一个非空格字符是'+'或'-',则跳过该字符
i++;
}
for(ret = 0; isdigit(s[i]); i++) // 从第一个非空格字符开始,如果是数字,则将其转换为整数
ret = (s[i]-'0') + ret*10; // 将字符转换为数字,累加到ret中
return ret * sign; // 返回最终结果
}
int myAtoi(char * s){
int i=0;
int sign=1;
long long ret=0;
while(isspace(s[i])) i++;
if(s[i]=='-') {
sign=-1;
i++;
} else if(s[i]=='+') {
i++;
}
while(isdigit(s[i])) {
ret=ret*10+(s[i]-'0');
if(ret*sign>INT_MAX) return INT_MAX;
if(ret*sign<INT_MIN) return INT_MIN;
i++;
}
return ret*sign;
}
14、遍历单链表中确定值最大的点
int GetMax(LinkList L)
{
// 如果链表为空,返回NULL
if(L->next == NULL)
return NULL;
LNode *pmax,*p;
// 假定第一个节点中数据最大
pmax=L->next;
p=L->next->next;
while(p)
{
// 若p值大于pmax值,则重新赋值
if(p->data > pmax->data)
pmax=p;
// 遍历链表
p=p->next;
}
// 返回最大值
return pmax->data;
}
应对嵌入式校招面试手撕之——链表_嵌入式手撕算法_iamsohard的博客-CSDN博客
程序员校招准备嵌入式软件面试的手撕代码练习_嵌入式软件会手撕tcp_iamsohard的博客-CSDN博客
1.分治法基本思想
分治法的基本思想是将一个规模为n的问题分解为k个规模为较小的子问题,这些子问题互相独立且与原问题相同。递归地求解这些子问题,然后利用子问题的解合并(构造)出原问题的解。
(1)分治算法的设计
分治算法的设计过程分为三个阶段:
分解(Divide) 阶段;将整个问题划分为多个子问题;
递归求解(Conquer)阶段: (递归调用正在设计的算法) 求解每个子问题;
合并( Combine)阶段:合并子问题的解,形成原始问题的解。
滑动窗口
滑动窗使用思路(寻找最长)
一核心: 左右双指针(L, R)在起始点,R向右逐位滑动循环
—每次滑动过程中
如果:窗内元素满足条件, R向右扩大窗口,并更新最优结果
如果:窗内元素不满足条件, L向右缩小窗口
—R到达结尾
滑动窗使用思路(寻找最短)
—核心:左右双指针(L, R)在起始点,R向右逐位滑动循环
—每次滑动过程中
如果:窗内元素满足条件, L向右缩小窗口,并更新最优结果
int minSubArrayLen(int target, int* nums, int numsSize){
int left=0,right=0;
int sum=0;
int best=0;
int*result=malloc(sizeof(int)*numsSize);
while(right<numsSize)
{
sum+=nums[right];
while(sum>=target)
{
if(right-left+1<best||best==0)
{
best=right-left+1;
}
sum-=nums[left];
left++;
}
right++;
}
return best;
}
24. 两两交换链表中的节点
struct ListNode* swapPairs(struct ListNode* head){
// 如果链表为空或只有一个节点,直接返回
if (head == NULL || head->next == NULL) {
return head;
}
// 定义两个指针p和q,分别指向相邻的两个节点
struct ListNode *p = head;
struct ListNode *q = head->next;
// 递归调用swapPairs函数,交换后面的节点
p->next = swapPairs(q->next);//p->next等于2指向的下一个结点交换后的结点,即swapPairs(q->next)
// 将q节点指向p节点,完成交换
q->next = p;//2指向1
// 返回交换后的链表头节点
return q;
}
3. 无重复字符的最长子串
int lengthOfLongestSubstring(char *s) {
int n = strlen(s);
int ans = 0;
int i = 0, j = 0;
int freq[128] = {0}; // 用于记录字符出现的频率
while (i < n && j < n) {
if (freq[s[j]] == 0) { // 如果当前字符没有出现过
freq[s[j++]]++; // 将字符加入窗口,并将其出现次数加1
ans = fmax(ans, j - i); // 更新最长子串长度
} else { // 如果当前字符已经出现过
freq[s[i++]]--; // 将窗口左侧的字符移出,并将其出现次数减1
}
}
return ans;
}
86. 分隔链表
该代码的思路是创建两个新的链表,一个用于存储小于x的节点,一个用于存储大于或等于x的节点。然后遍历原链表,将小于x的节点插入到小链表中,将大于或等于x的节点插入到大链表中。最后将小链表的尾节点指向大链表的头节点,返回小链表的头节点即可。
// 定义单链表结构体
// 包含一个整数值和指向下一个节点的指针
struct ListNode {
int val;
struct ListNode *next;
};
// 分割链表函数,传入链表头指针和分割值x
struct ListNode* partition(struct ListNode* head, int x) {
// 如果链表为空或只有一个节点,直接返回
if(head==NULL||head->next==NULL)
return head;
// 定义两个新链表头节点,一个存放小于x的节点,一个存放大于等于x的节点
struct ListNode *smallHead = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode *bigHead = (struct ListNode*)malloc(sizeof(struct ListNode));
// 定义两个尾指针,用于添加新节点
struct ListNode *smallTail = smallHead;
struct ListNode *bigTail = bigHead;
// 定义一个指针p,用于遍历原链表
struct ListNode *p = head;
// 遍历原链表
while (p != NULL) {
// 如果当前节点的值小于x,将其添加到小链表中
if (p->val < x) {
smallTail->next = p;
smallTail = smallTail->next;
} else { // 否则将其添加到大链表中
bigTail->next = p;
bigTail = bigTail->next;
}
// 指针p指向下一个节点
p = p->next;
}
// 将小链表的尾节点指向大链表的头节点
smallTail->next = bigHead->next;
// 将大链表的尾节点指向NULL
bigTail->next = NULL;
// 如果小链表为空,直接返回大链表
if(smallHead->next == NULL) return bigHead->next;
// 否则返回小链表
return smallHead->next;
}
141. 环形链表
bool hasCycle(struct ListNode *head) {
int count = 10001; // 设置一个计数器,避免链表太长导致死循环
while (count) { // 循环10001次
if (head == NULL) { // 如果链表为空,说明没有环
return false;
}
head = head->next; // 指针向后移动
count--; // 计数器减1
}
return true; // 如果循环10001次还没有返回false,说明链表有环
}
19. 删除链表的倒数第 N 个结点
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
// 定义函数,传入链表头指针和要删除的倒数第n个节点的位置n
if(head==NULL) return head;
// 如果链表为空,直接返回头指针
struct ListNode*fast=head;
// 定义快指针,指向头指针
struct ListNode*slow=head;
// 定义慢指针,指向头指针
while(n)
{
fast=fast->next;
n--;
}
// 快指针先走n步
if(fast==NULL) return head->next;
// 如果快指针已经走到链表末尾,说明要删除的是头节点,直接返回头指针的下一个节点
while(fast->next!=NULL)
{
fast=fast->next;
slow=slow->next;
}
// 快指针和慢指针同时向前走,直到快指针走到链表末尾
slow->next=slow->next->next;
// 慢指针的下一个节点指向下下个节点,即删除了倒数第n个节点
return head;
// 返回头指针
}
61. 旋转链表
struct ListNode* rotateRight(struct ListNode* head, int k){
if(head==NULL||head->next==NULL||k==0)
return head;
int count=1;
struct ListNode*p=head;
while(p->next!=NULL)
{
p=p->next;
count++;//计算链表结点个数
}
p->next=head;//链表尾指向头,形成环形链表
int step=count-k%count;
while(step--)
{
p=p->next;//寻找新的链表尾
}
struct ListNode*tail=p;//链表尾
struct ListNode*res=p->next;//因为是环形链表,所以这是链表头
tail->next=NULL;//链表尾指向空
return res;
}
438. 找到字符串中所有字母异位词(滑动窗口)
int* findAnagrams(char * s, char * p, int* returnSize){
int len1=strlen(s);
int len2=strlen(p);
int*res=(int*)malloc(sizeof(int)*128);
if(len2>len1)
*returnSize = 0;
return res;
int i=0;
int count[26]={0};
for(int i=0;i<len1;i++)
{
count[p[i]-'a']++;
}
for(int left=0,right=0;right<len1;right++)
{
count[s[right]-'a']--;
while(count[s[right]-'a']<0)
{
count[s[left]-'a']++;
left++;
}
if(right-left+1==len2)
res[i++]=left;
}
* returnSize=i;
return res;
}
5. 最长回文子串
// 给定一个字符串s,返回s中的最长回文子串
char* longestPalindrome(char* s) {
int len = strlen(s); // 获取字符串s的长度
if (len < 2) { // 如果s的长度小于2,直接返回s
return s;
}
int maxLen = 1, start = 0; // 初始化最长回文子串的长度为1,起始位置为0
for (int i = 0; i < len - 1; i++) { // 遍历字符串s
int len1 = expandAroundCenter(s, i, i); // 以i为中心,向两边扩展,获取回文子串的长度
int len2 = expandAroundCenter(s, i, i + 1); // 以i和i+1为中心,向两边扩展,获取回文子串的长度
int curMaxLen = len1 > len2 ? len1 : len2; // 取两种情况下的最大回文子串长度
if (curMaxLen > maxLen) { // 如果当前回文子串长度大于最大回文子串长度
maxLen = curMaxLen; // 更新最大回文子串长度
start = i - (maxLen - 1) / 2; // 更新最大回文子串的起始位置
}
}
s[start + maxLen] = '\0'; // 将最大回文子串的结束位置设置为'\0',使其成为一个字符串
return s + start; // 返回最大回文子串的起始位置
}
// 以left和right为中心,向两边扩展,获取回文子串的长度
int expandAroundCenter(char* s, int left, int right) {
int len = strlen(s); // 获取字符串s的长度
while (left >= 0 && right < len && s[left] == s[right]) { // 如果left和right指向的字符相同,继续向两边扩展
left--; // 左指针向左移动
right++; // 右指针向右移动
}
return right - left - 1; // 返回回文子串的长度
}
2487. 从链表中移除节点
int findRepeatNumber(int* nums, int numsSize){
if(numsSize==0||numsSize==1) return -1;
for(int i=0;i<numsSize;i++)
{
while(nums[i]!=i)
{
if(nums[i]==nums[nums[i]])
{
return nums[i];
}
int k=nums[nums[i]];
nums[nums[i]]=nums[i];
nums[i]=k;
}
}
return -1;
}
剑指 Offer 45. 把数组排成最小的数
如果x+y<y+x,就让x排在y的前面。