题目大解析(2)

在这里插入图片描述
在这里插入图片描述


sizof代码分析

下面代码的结果是:

#include <stdio.h>
int i;
int main()
{
    i--;
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}

A.>
B.<
C.不输出
D.程序有问题

C语言中,0为假,非0即为真。
全局变量,没有给初始值时,编译其会默认将其初始化为0。
i的初始值为0,i–结果-1,i为整形,sizeof(i)求i类型大小是4,按照此分析来看,结果应该选择B,但是sizeof的返回值类型实际为无符号整形,因此编译器会自动将左侧i自动转换为无符号整形的数据,-1对应的无符号整形是一个非常大的数字,超过4或者8,故实际应该选择A

有序数合并

#include <stdlib.h>
int change(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}
int main()
{
	int arr1[1000] = { 0 };
	int arr2[1000] = { 0 };
	int n, m;
	scanf("%d %d", &n, &m);
	int i = 0,j=0;
	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr1[i]);
	}
	for (i = n; i < (m+n); i++)
	{
		scanf("%d", &arr1[i]);
	}
	//冒泡排序
	//for (i = 0; i < m + n-1; i++)
	//{
	//	for (j = 0; j < m + n - i-1; j++)
	//	{
	//		if (arr1[j] > arr1[j+1])
	//		{
	//			int tmp = arr1[j];
	//			arr1[j] = arr1[j+1];
	//			arr1[j+1] = tmp;
	//		}
	//	}
	//}
	
	//qsort排序
	qsort(arr1, m + n, sizeof(arr1[0]), change);
	for(i = 0; i < m + n; i++)
	{
		printf("%d ", arr1[i]);
	}


	return 0;
}

BC105-矩阵相等判断

int main() {
    int arr1[30][30] = { 0 };
    int arr2[30][30] = { 0 };
    printf("请输入两个整数n和m:");
    int n, m;
    scanf("%d %d", &n, &m);
    int i = 0, j = 0;
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            scanf("%d", &arr1[i][j]);
        }
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            scanf("%d", &arr2[i][j]);
        }
    }
    //接下来判断两个矩阵的对应元素是否相等
    int flag = 1;
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            if (arr1[i][j] != arr2[i][j])
            {
                flag = 0;
                goto end;
            }
        }
    }
end:
    if (flag == 1)
        printf("Yes\n");
    else
        printf("No\n");

    return 0;
}

BC98-序列中删除指定数字

#include <stdio.h>

int main() {
     int n=0,m=0;
     int arr[50]={0};
     scanf("%d",&n);
     if(!(n>=1&&n<=50))
     {
        return 0;
     }
     int i=0;
     for(i=0;i<n;i++)
     {
        scanf("%d",&arr[i]);
     }
     scanf("%d",&m);
      for(i=0;i<n;i++)
     {
        if(arr[i]!=m)
        {
            printf("%d ",arr[i]);
        }
     }
    return 0;
}

BC54-获得月份天数

int get_days(int n,int m)
{
    if(m==4||m==6||m==9||m==11)
    return 30;
    else if(m==2)
    {
        	if ((n % 100 == 0 && n % 400 == 0) || (n % 4 == 0 && n % 100 != 0))
		return 29;//闰年返回29
        else
         return 28;
    }
    else
    return 31;
}
int main() {
    int year,month;
    while(scanf("%d %d",&year,&month)==2)
   {
     int days=get_days(year,month);
    printf("%d\n",days);
   }
    
    return 0;
}

BC37-网购

int main() {
    float price=0;
    int n,m,k;//日期,优惠卷
    float total=0;
    scanf("%f %d %d %d",&price,&n,&m,&k);
    if(!(1<=price&&price<=100000)||(k!=1&&k!=0))
    {
        return 0;
    }
    if(n==11||n==12)
    {
        if(n!=m)
        return 0;
    }
    if(n==11&&m==11)
    {
        total=price*0.7;
    }
    else {
    total=price*0.8;
    }
    if(k==1)
    {
        if(total-50>0)
        printf("%.2f\n",total-50);
        else
         printf("0.00\n");

    }
    else
    printf("%.2f\n",total);
    return 0;
}

字符串左旋

法一:三步法

#include <stdio.h>
#include <string.h>
void Spin(char arr[], int n)
{
	//三步法
	int i = 0;
	int len = strlen(arr);//字符串长度
	n %= len;//防止冗余
	for (i = 0; i < n; i++)//左旋n个字符
	{
		//接下来是左旋一个字符的动作代码
		//首先要将左旋的字符存到某个变量当中,然后将整个字符串向左位移
		int j = 0;//将左旋的字符存到某个变量当中
		int tmp = arr[0];
		for (j = 0; j < len-1; j++)
		{
			arr[j] = arr[j + 1];
		}
		arr[len-1] = tmp;
	}
}
int main()
{
	char arr[20]="abcdef";
	int n = 0;
	scanf("%d", &n);
	Spin(arr, n);
	printf("%s\n", arr);
	return 0;
}

法二:逆序法

void reverse(char* arr, int n)
{
	char* left = arr;
	char* right = arr + n - 1;
	while (left < right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}
void Spin(char arr[], int n)
{
	int len = strlen(arr);
	n %= len;//防止冗余
	reverse(arr,n);//逆序要左旋的字符
	reverse(arr+n,len-n);//逆序剩下的字符串
	reverse(arr,len);//再逆序整个字符串
}
int main()
{
	char arr[20]="abcdef";
	int n = 0;
	scanf("%d", &n);
	Spin(arr, n);
	printf("%s\n", arr);
	return 0;
}

逆序法也可以很容易推得右旋

杨氏矩阵

有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
要求:时间复杂度小于O(N);

int find_num(int arr[][3], int r, int l, int n)
{
	//设右上角的数为k,从右上角开始判断,若n>k,则该行抹去无需再查找,往下一行
	//直到遇到k>n,此时行不再减少了,开始判断列,若n<k,此时列也无需再查找了,删去该列,直到遇见n>k
	//当n=k时,循环停止
	int x = 0;
	int y = l-1;//假设没有
	while (x<=r-1&&y>=0)
	{
		if (arr[x][y] < n)
		{
			x++;
		}
		else if (arr[x][y] > n)
		{
			y--;
		}
		else
			return 1;
	}
	return 0;
}
int main()
{
	int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
	int sz_1 = sizeof(arr) / sizeof(arr[0]);
	int sz_2 = sizeof(arr[0]) / sizeof(arr[0][0]);
	int n = 0;
	scanf("%d", &n);
	if (find_num(arr, sz_1, sz_2, n))
		printf("找到了\n");
	else
		printf("没找到\n");

	return 0;
}

find_num中将参数arr[][3]设置为列大于3,会出现什么情况
例如我设置为4
arr[3][3]中一共有36字节数据存储,现在放到48字节二维数组中存储,原本1,2,3在第一行,现在第一行变为了1,2,3,4.
在这里插入图片描述

我们之前讲二维数组有讲过,二维数组在内存空间中所有行都在同一行上,所以,列其实不会有越界情况,只是访问到了"下一行"的元素。

malloc和struct(分配空间)

#define MAX_SIZE A+B
struct _Record_Struct
{
  unsigned char Env_Alarm_ID : 4;
  unsigned char Para1 : 2;
  unsigned char state;
  unsigned char avail : 1;
}*Env_Alarm_Record;
struct _Record_Struct *pointer = (struct _Record_Struct*)malloc(sizeof(struct _Record_Struct) * MAX_SIZE);

当A=2, B=3时,pointer分配( )个字节的空间。

代码分析:
我们知道位段申请空间是一字节或4字节申请(不够再申请),首先申请1字节的空间,Env_Alarm_ID : 4和Para1 : 2共占6bit,state所需要8bit空间,一开始申请的1字节空间显然不够用了,再申请1字节空间此时正好存放state,此时还差avail : 1,于是再申请1字节空间,avail : 1独占1字节空间。
至此,整个结构体一共申请了3字节的空间,结构体的大小为3字节

注意:在宏定义中是不会进行计算的
sizeof(struct _Record_Struct) * MAX_SIZE应变为
3x2+3,最终结果为9
所以此时malloc所申请的空间大小为9,pointer所分配的空间大小为9。

判断输出结果

下面代码的结果是( )

int main()
{
  unsigned char puc[4];
  struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);
  return 0;
}

代码分析
根据上道题的思路,我们可以易知在该代码中结构体所申请的空间大小为2字节

 pstPimData = (struct tagPIM*)puc;

因此pstPimData指针所能访问的最大地址长度为2字节
在这里插入图片描述
注意:
1.%x:打印16进制数
2.%02x:打印16进制数,打印两位,若不足两位,0补位
3.%2x:打印16进制数,打印两位,若不足两位,空格补位

模拟实现函数atoi

#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <limits.h>
enum state
{
	VALID,
	INVALID
};
enum state s = INVALID;//一开始先设置非法状态,等转换成功后在设置为合法状态
int my_atoi(const char* str)
{
	//1.空指针情况
	assert(str);
	//2.空字符串情况
	if (*str == '\0')//但其实这里不严谨,到底是遇到\0还是遇到字符0,不能确定,所以我们需要创建一个枚举变量
	{
		return 0;
	}
    //3.遇到 -号问题
	int flag = 1;
	if (*str == '-')
	{
		flag = -1;
		str++;
	}
	//接下来开始正式转换
	long long ret = 0;//为了防止数据太大溢出,用更大类型的long long来接收
	while (*str)
	{
		if (isdigit(*str))
		{
			ret = ret * 10 + flag * (*str - '0');
			if (ret > INT_MAX || ret < INT_MIN)
			{
				return (int)ret;
			}
		}
		else
		{
			return (int)ret;//遇到非数字字母的直接返回
		}
		str++;
	}
	//到了这一步说明转换成功,将状态改为合法
	s = VALID;
	return (int)ret;
}

int main()
{
	char arr[20];
	gets(arr);
	int ret = my_atoi(arr);
	if (s == INVALID)
		printf("转换非法:%d\n", ret);
	else
		printf("转换合法:%d\n", ret);
	return 0;
}

定义两个指针

int* a,b;
//大部分人看到这种定义,一定认为a,b的类型被定义为int*,实际上a类型被定义为int*,b为int
//它的实际代码效果应该这么看
int* a;
int b;

小知识:在头文件最好不要定义全局变量,因为可能会造成重定义

offsetof宏

offsetof

offsetof (type,member)
返回成员偏移量
参数:
1.类型
type应为结构或联合类型
2.成员:
类型的成员
返回值
类型为 size_t 的值,其偏移值为类型中的成员。
头文件:<stddef.h>

模拟实现宏offsetof:

#define OFFSETOF(type,name)  (size_t)&(((type*)0)->name)//地址从0开始
struct S
{
	char  a;
	int b;
	int c;
};
int main()
{
	printf("%d\n", OFFSETOF(struct S, a));
	printf("%d\n", OFFSETOF(struct S, b));
	printf("%d\n", OFFSETOF(struct S, c));

	return 0;
}

在这里插入图片描述

交换奇偶位(宏)

#define Swap_Bit(x)  ((x&0x55555555)<<1)+((x&0xaaaaaaaa)>>1)
int main()
{
	int a = 5;
	printf("%d\n", Swap_Bit(a));
}

在这里插入图片描述

轮转数组

超出时间限制版本

void rotate(int* nums, int numsSize, int k){
    int i=0,j=0;
     k%=numsSize;//k可能大于numsSize,所以要取余
        for( i=0;i<k;i++)
    {
       int tmp1=nums[numsSize-1];
       for(j=numsSize-2;j>=0;j--)
       {
           int tmp2=nums[j];
           nums[j+1]=nums[j];
           if(j-1>=0)
           {
               nums[j]=nums[j-1];
           }
       }
       nums[0]=tmp1;
    }
    
}

reverse版本

void reverse(int *p1,int *p2){
    while(p1<p2)
    {
        int tmp=*p1;
        *p1=*p2;
        *p2=tmp;
        p1++;
        p2--;
    }
}
void rotate(int* nums, int numsSize, int k){
     k%=numsSize;//k可能大于numsSize,所以要取余
     reverse(nums,nums+numsSize-1);
     reverse(nums+k,nums+numsSize-1);
     reverse(nums,nums+k-1);
}

memcpy版本

void rotate(int* nums, int numsSize, int k)
{
    k%=numsSize;
    int* tmp=(int*)malloc(sizeof(int)*numsSize);
    memcpy(tmp+k,nums,sizeof(int)*(numsSize-k));
    memcpy(tmp,nums+numsSize-k,sizeof(int)*k);
    memcpy(nums,tmp,sizeof(int)*numsSize);//再memcpy回去
}

消失的数字

异或版本

int missingNumber(int* nums, int numsSize){
     int i=0;
     int n=0;
     //如输入[3,0,1]
     for(i=0;i<=numsSize;i++)
     {
         n^=i;
     }
      // n=0^0^1^2^3
     for(i=0;i<numsSize;i++)
     {
         n^=nums[i];
     }
     //n^3^0^1=0^0^1^2^3^3^0^1
     //因为异或的0^0=0,0^x=x,x^x=0
     //最后异或的结果就是缺少的那个数字
     return n;
}

求和法

int missingNumber(int* nums, int numsSize){
     int sum=0;
     int i=0;
     for(i=0;i<=numsSize;i++{
       sum+=i;
     }
      for(i=0;i<numsSize;i++{
       sum-=nums[i];
     }
     return sum;
}

移除元素

法一:Erase

#include <assert.h>
int Find(int* p, int val, int* sz)
{
    for (int i = 0; i < *sz; i++)
    {
        if (p[i] == val)
            return i;
    }
    return -1;
}
void Erase(int* p, int val, int* sz)
{
    assert(*sz > 0);
    int i = 0;
    int pos = Find(p, val, sz);
    if (pos == -1)
        return;
    int num = pos;
    for (i = 0; i < *sz - pos - 1; i++)
    {
        p[num] = p[num + 1];
        num++;
    }
    (*sz)--;
}
int removeElement(int* nums, int numsSize, int val) {
    int i = 0;
    int* p = nums;
    int sz = numsSize;
    int count = 0;
    for (i = 0; i < sz; )
    {
        i++;
        if (nums[i-1] == val)
        {
            Erase(p, val, &sz);
            i = 0;
            count++;
        }
    }
    nums = p;
   /* for ( i=0; i < numsSize - count; i++)
    {
        printf("%d ", nums[i]);
    }*/
    return numsSize - count;
}

法二:

int removeElement(int* nums, int numsSize, int val) {
    int dest = 0;
    int src = 0;
    while (src < numsSize)
    {
        if (nums[src] != val)
        {
            nums[dest++] =nums[src++];
        }
        else
            src++;
    }
    return dest;
}

合并两个有序数组

示例一

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

#include <stdlib.h>
int fun(const void* p1,const void* p2)
{
    return *(int*)p1-*(int*)p2;
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
       int i=0,j=0;
       for(i=m;i<m+n;i++,j++)
       {
           nums1[i]=nums2[j];
       }
       qsort(nums1,m+n,sizeof(nums1[0]),fun);

}

法二:归并尾插



void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
     int end1=m-1;
     int end2=n-1;
     int i=m+n-1;
     while(end1>=0&&end2>=0)
     {
          if(nums1[end1]>nums2[end2])
          {
              nums1[i]=nums1[end1];
              i--;
              end1--;
          }
          else
          {
               nums1[i]=nums2[end2];
               i--;
              end2--;
          }
     }
     while(end2>=0)
     {
         nums1[i]=nums2[end2];
               i--;
              end2--;
     }
}

删除有序数组中的重复项

在这里插入图片描述

版本一:

void Erase(int* nums, int* sz, int val, int count)
{
    int dest = 0;
    int src = 0;
    int num = count;
    while (src < *sz)
    {
        if (nums[src] != val || count == 0)
        {
            nums[dest++] = nums[src++];
        }
        else
        {
            src++;
            if (count > 0)
                count--;
        }
    }
    *sz = *sz - num;
}
int removeDuplicates(int* nums, int numsSize)
{
    for (int i = 0; i < numsSize; i++)
    {
        int flag = 0, count = 0;
        for (int j = i + 1; j < numsSize; j++)
        {
            if (nums[j] == nums[i])
            {
                flag = 1;
                count++;
            }
        }
        if (flag)
        {
            Erase(nums, &numsSize, nums[i], count);
        }
    }
    return  numsSize;
}

版本二:优化后

int removeDuplicates(int* nums, int numsSize)
{
    int count=1, k=0;
    for(int i=1; i<numsSize; i++)
    {
        if(nums[i]!=nums[k])
        {
            nums[++k] = nums[i];
            count++;
        }
    }
    return count;

}

罗马数字转整数

在这里插入图片描述

int GetNum2(char* str)
{
    int ret=0;
    if(*str=='I')
    ret=1;
    else if(*str=='V')
    ret=5;
     else if(*str=='X')
    ret=10;
     else if(*str=='L')
    ret=50;
     else if(*str=='C')
    ret=100;
     else if(*str=='D')
    ret=500;
     else if(*str=='M')
    ret=1000;
    return ret;
}
int Compare(char* str1,char* str2)
{
    int a=GetNum2(str1),b=GetNum2(str2);
      return a-b;
}
// int GetNum1(char c1,char c2)
// {
//     if(c1=='V')
//     return 4;
//     else
//     return 9;
// }

int romanToInt(char * s){
     int sum=0;
     while(*s)
     {
         if(Compare(s+1,s)>0)
         {
             sum+=Compare(s+1,s);
             s+=2;
         }
         else
         {
             sum+=GetNum2(s);
             s++;
         }
     }
     return sum;
}

自守数

牛客链接自守数

#include <stdio.h>
#include <math.h>
int Judge(int x)
{
    int num = pow(x,2);
    while(x)
    {
        int cmp1 = num%10;
         int cmp2 = x%10;
         if(cmp1!= cmp2)
         return 0;
         num/=10;
         x/=10;
    }
    return 1;
}
int main() {
    int n = 0;
    int i = 0;
    int count = 0;
    scanf("%d", &n);
    for (i = 0; i <= n; i++)
    {
        if(Judge(i))
        count++;
    }
    printf("%d", count);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值