字符串
关于strtok函数的使用
int main()
{
char arr1[] = "lich@jmu.edu.student";
char arr2[100] = { 0 };//临时数据
char sep[] = "@.";
strcpy(arr2, arr1);
char* ret = NULL;
//分割字符串
for (ret=strtok(arr2, sep); ret!=NULL; ret=strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
字符串的左旋
- 实现一个函数,可以左旋字符串中的k个字符:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
#include<stdio.h>
#include<string.h>
void leftMove(char *ch, int k,int len) {
int i = 0;
//趟数
for (i = 0; i < k; i++) {
int j = 0;
//左移
char temp = ch[0];
for (j = 1; j < len; j++) {
ch[j - 1] = ch[j];
}
ch[len - 1] = temp;
}
}
int main() {
char ch[] = "abcdefg";
int len = strlen(ch);
int k = 0;
scanf("%d", &k);
leftMove(ch, k,len);
printf("%s\n", ch);
return 0;
}
下面我们也可以用逆序字符串的方式,进行三次旋转,然后得到翻转后的字符串:
#include<stdio.h>
#include<string.h>
#include<assert.h>
void reverse(char* left, char* right)
{
assert(left && right);
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
void left_move(char* arr, int k)
{
assert(arr);
int len = strlen(arr);
k %= len;
reverse(arr, arr+k-1);//逆序左边
reverse(arr+k, arr+len-1);//逆序右边
reverse(arr, arr+len-1);//逆序整个字符串
}
int main()
{
char arr[] = "abcdef";
//左旋转字符串
left_move(arr, 2);
printf("%s\n", arr);//打印字符串
//cdab
return 0;
}
判断一个字符串是否另一个字符串旋转得来的
#include<stdio.h>
#include<string.h>
#include<assert.h>
int is_leftMove(char *arr1, char *arr2) {
assert(arr1 && arr2);
int len1 = strlen(arr1);
int len2 = strlen(arr2);
if (len1 != len2) {
return 0;
}
//两个arr1连在一块
strncat(arr1, arr1, len1);
if (NULL == strstr(arr1, arr2)) {
return 0;
}
else {
return 1;
}
}
int main() {
char arr1[] = "AABCD";
char arr2[] = "BCDAA";
int ret = is_leftMove(arr1, arr2);
if (ret == 1) {
printf("YES\n");
}
else {
printf("NO\n");
}
return 0;
}
矩形递增数组找数字
有一个数字矩阵,矩阵的每行从左到右是递增的,
矩阵从上到下是递增的,
请编写程序在这样的矩阵中查找某个数字是否存在。
复杂度为O(n);
#include<stdio.h>
void findK(int a[3][3], int row, int col, int k) {
int x = 0;
int y = col - 1;
while (x < row && y >= 0)
{
if (a[x][y] < k)
x++;
else if (a[x][y] > k)
y--;
else
{
printf("找到了,下标是:%d %d\n", x, y);
return;
}
}
printf("找不到\n");
}
int main() {
int a[3][3] = { 1,2,3,4,5,6,7,8,9 };
int k = 0;
scanf("%d", &k);
findK(a, 3, 3, k);
return 0;
}
模拟实现strcpy函数
我们可以查出:strcpy的内部函数定义为: char *strcpy(char *dest,*src);
下面我们可以实现strcpy的函数如下:
#include<stdio.h>
#include<assert.h>
char * my_strcpy(char *dest, char *src) {
assert(dest && src);
char *ret = dest;
while (*dest++ = *src++) {
;
}
return ret;
}
int main() {
char ch1[20] = {'0'};
char ch2[] = "abcedfg";
my_strcpy(ch1, ch2);
printf("%s\n", ch1);
return 0;
}
模拟实现strcat函数
strcat的内部函数表示为:char * strcat(char *dest,char *src);
char *my_strcat(char *dest, char *src) {
assert(dest && src);
char *ret = dest;
while (*dest) {
dest++;
}
while (*src) {
*dest++ = *src++;
}
return ret;
}
int main() {
char arr1[50] = "abcdefg";
char arr2[] = "hijklmn";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
模拟实现strcmp函数
strcmp函数的内部函数如下:
int srtcmp(const char s1,const char s2);
int my_strcmp(const char *arr1, const char* arr2)
{
assert(arr1 &&arr2);
while (*arr1 == *arr2) {
if ( * arr1 == '\0')
return 0;
arr1++;
arr2++;
}
return *arr1 - *arr2;
}
int main() {
char arr1[] = "abcdefg";
char arr2[] = "abcdefz";
int ret=my_strcmp(arr1, arr2);
if (ret == 0) {
printf("两个字符串大小相等\n");
}
else if(ret > 0){
printf("arr1的字符串大\n");
}
else {
printf("arr2的字符串小\n");
}
return 0;
}
模拟实现strncpy函数
我们可以根据strcpy函数来进行strncpy函数的构造为:
char *strncpy(char *dest,char *src,int k);
char *my_strncpy(char *dest, char *src, int k) {
assert(dest && src);
char *ret = *dest;
while (k--) {
*dest = *src;
dest++;
src++;
}
return ret;
}
int main() {
char arr1[] = "abcedf";
char arr2[] = "ghijkl";
int len = strlen(arr1);
int k = 0;
scanf("%d", &k);
k %= len;
my_strncpy(arr1, arr2, k);
printf("%s", arr1);
return 0;
}
模拟实现strncat函数
根据strcat函数,我们可以构造char *strncat(char *dest,char *src,int k);的函数实现其功能。
char *my_strncat(char *dest, char *src, int k) {
assert(dest && src);
char *ret = dest;
while (*dest) {
dest++;
}
while (k--) {
*dest++ = *src++;
}
return ret;
}
int main(){
char arr1[20] = "abcdefg";
char arr2[20] = "hijklmn";
int len = strlen(arr2);
int k = 0;
scanf("%d", &k);
k %= len;
printf("%s\n", my_strncat(arr1, arr2, k));
return 0;
}
模拟实现strncmp函数
根据strncmp函数,我们可以构造char *strncnp(const char *s1,const char *s2,int k);的函数实现其功能。
综上的int k 笔者认为size_t更为合适。
int my_strncmp(const char *s1, const char *s2, int k) {
assert(s1 && s2);
while (*s1&&*s2 && (*s1 == *s2) && k--)
{
s1++;
s2++;
}
return *s1 - *s2;
}
int main() {
char arr1[20] = "abcdfg";
char arr2[20] = "abcdef";
int k = 0;
scanf("%d", &k);
k %= strlen(arr1);
int ret = my_strncmp(arr1, arr2, k);
if (ret == 0) {
printf("arr1=arr2 \n");
}
else if (ret > 0) {
printf("arr1>arr2");
}
else {
printf("arr1<arr2");
}
return 0;
}
模拟实现strstr函数
我们根据C语言文档可以得知:strstr函数内部实现为:char strstr(const char str1,const char *str2);
根据KMP算法,我们实现strstr函数。
char *my_strstr(const char *str1,const char *str2) {
assert(str1 && str2);
char *cp = str1;
char *s1;
char *s2;
if (*str2 == '\0')
return str1;
while (*cp) {
s1 = cp;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2) {
s1++;
s2++;
}
if (*s2 == '\0') {
return cp;
}
cp++;
}
return NULL;
}
int main()
{
char arr1[] = "i am good student, hehe student";
char arr2[] = "student";
//查找arr1中arr2第一次出现的位置
char *ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
模拟实现memcpy函数
我们根据C语言文档可以得知,memcpy函数的内部实现为:void* memcpy(void* dest, const void*src, size_t count)
memcpy不可以实现重叠部分的copy,可以使用memmove函数实现重叠部分的copy。
模拟函数如下:
#include<stdio.h>
#include<string.h>
#include<assert.h>
void * my_memcpy(void *dest, const void* src, size_t count) {
void *ret = dest;
assert(dest && src);
while (count--) {
*(char *)dest = *(char *)src;
dest = (char *)dest + 1;
src = (char *)src + 1;
}
return ret;
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
//拷贝的是整型数据
my_memcpy(arr2, arr1, 10*sizeof(int));
int i = 0;
for (i = 0; i < 20; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
模拟实现memmove函数
在上面的memcpy函数我们提到memcpy函数只能实现不重叠的复制,而memmove函数可以实现重叠的部分的复制,其内部函数的构造和memcpy函数的定义的参数都是一样的:void *memmove(void *dest,void *src,size_t count);但是由于涉及重叠部分的复制,引起其实现的过程是更为复杂的。下面我们来分析一下其实现过程:
当dest在前面时,将src中的数据只能一步步从前往后的复制,因为只有先复制重叠的部分地址的数据才能避免后面因为重叠部分复制了新的内容而覆盖掉原来的内容的风险。(这句话太绕了,要好好读几遍,笔者真的思考好久怎么表达它,才能把它表达清楚。),所以我们在这种情况下就是从前往后一次复制了。
第二种情况就是目标地址在源地址的后面,我们的思路还是首先考虑重叠部分的优先复制,原因还是避免重叠部分还没来得及被复制到目标地址前就被覆盖掉:这一次我们就是从后往前复制了。
话不都说,我们直接上代码:
void *my_memmove(void *dest, void *src, size_t count) {
void *ret = dest;
assert(dest && src);
if (dest < src) {
while (count--) {
*(char *)dest = *(char *)src;
dest = (char *)dest + 1;
src = (char *)src + 1;
}
}
else {
while (count--) {
*((char *)dest + count) = *((char *)src + count);
}
}
return ret;
}
int main() {
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1+2, arr1 , 16);
for (int i = 0; i < 10; i++) {
printf("%d ", arr1[i]);
}
printf("\n");
my_memmove(arr1, arr1+2, 16);
for (int i = 0; i < 10; i++) {
printf("%d ", arr1[i]);
}
return 0;
}
模拟实现memset函数
void* my_memset(void *str, int c, size_t count) {
assert(str);
void *ret = str;
while (count--) {
*((char *)ret) = (char)c;
ret = (char*)ret + 1;
}
return ret;
}