本着学好C语言的目的,写下此贴,现在发现,如果想学好编程,一些必要的算法是必须要背过的,因为这么简洁清晰的代码是普通初学者根本想不出来的。
1.scanf输入(需用回车+Ctrl+Z+回车实现完成输入)
while(scanf(“%d”,&x)==1){...}
scanf也是有返回值的,返回值为1代表成功输入了。
2.最大回文字符串(原字符串是一个整串)(此题最重要的部分就是函数的有效利用)
方法:1.清除格式(预处理,转换大小写,合理利用一些函数,可以更加简洁)->判断(中间处理)->输出
清除格式:用<ctype.h>中的几个函数:isalpha(判断大小写),isdigit(判断十进制数字),isprint(判断是否为可打印字符),toupper(返回字符大写形式),tolower(返回字符小写形式)等对每个字母依次判断。
n = strlen(buf);
m = 0;
for(i = 0; i < n; i++)
{
if(isalpha(buf[i])) s[m++] = toupper(buf[i]);
}
然后判断出其中最大的回文串,常规操作,并没有什么窍门。
int i,j,max = 0;
for(i = 0; i < m; i++)
for(j = i; j < m; j++)
if(isHuiWenChuan(buf[i],buf[j]) && j-i+1 > max)
max = j-i+1;
最后,判断回文串的函数isHuiWenChuan:
for(k = i; k < j; k++)
if(buf[i] != buf[j-i]) bool = 0;
其中,bool是一个判断变量,一旦两个字符不相等,那么它就会设为0代表不是回文串。
这样,就获得了最大回文串的长度,这时仍不知道它的具体位置,这时就用上了最初定义的p数组,它储存的是每一个字母在最初字符串的位置,它的下标是每一个字母在新字符串中的位置。这个处理要在之前提到的预处理中执行。总的代码如下:
#include <stdio.h>
#include <String.h>
#include <ctype.h>
#define MAXN 1000 + 10
char buff[MAXN],s[MAXN];
int p[MAXN];
int main(){
int i,j,n,k,m = 0,bool = 1,a,b;
scanf("%s",buff);
n = strlen(buff);
for(i = 0; i < n; i++)
{
if(isalpha(buff[i]))
{
p[m] = i;
s[m++] = toupper(buff[i]);
}
}
for(i = 0; i < n; i++)
{
for(j = i; j < n; j++)
{
bool=1;
for(k = i; k <= j; k++)
{
if(s[k] != s[i+j-k]) bool = 0;
if(bool && i+j-k > m) {m=i+j-1; a = p[i]; b = p[j];}
}
}
}
for(i = a; i <= b; i++)
printf("%c",buff[i]);
printf("\n");
}
这段代码有效率问题,运行不快,优化后的代码还恶心,这时是从中间向两边拓展,然后区分单复数,这个运行还快些。
#include <stdio.h>
#include <String.h>
#include <ctype.h>
#define MAXN 1000 + 10
char buff[MAXN],s[MAXN];
int p[MAXN];
int main(){
int i,j,n,k,m = 0,bool = 1,a,b;
scanf("%s",buff);
n = strlen(buff);
for(i = 0; i < n; i++)
{
if(isalpha(buff[i]))
{
p[m] = i;
s[m++] = toupper(buff[i]);
}
}
for(i = 0; i < n; i++)
{
for(j = 0; i-j >= 0 && i+j < n; j++)
{
if(buff[i-j] != buff[i+j]) break;
if(buff[i-j] == buff[i+j] && j*2+1 > m){a = buff[i-j]; b = buff[i+j]; m = j*2+1;}
}
for(j = 0; i-j >=0 && i+j+1 <n;j++)
{
if(buff[i-j] != buff[i+j+1]) break;
if(buff[i-j] == buff[i+j+1]) {a = p[i-j]; b = p[i+j+1];}
}
}
for(i = a; i <= b; i++)
printf("%c",buff[i]);
printf("\n");
}
也可以分别单列成函数,预处理函数和处理函数,同用一个变量,耦合性比较强。
#include<stdio.h>
#include<string.h>
#include<ctype.h> //会用到其中的一些函数
#define MAXN 5000 + 10 //多10个保险
struct String //这样定义一个字符串会很方便//并不是很明智的
{
char str[MAXN];
int n;
};
struct String str1, str2; //str1用来放最初字符串,str2用来放处理后的字母
int a, b; //标记最大回文串在原字符串中的位置
int p[MAXN]; //用来标记str2中每个字符在str1中的位置
int function1(struct String string1,struct String string2);
void function2(struct String string);
void function3();
int main()
{
memset(str1.str,0,sizeof(str1.str));//初始化str1
memset(str2.str,0,sizeof(str2.str));//初始化str2
scanf("%s",str1.str);
str1.n = strlen(str1.str);
str2.n = function1(str1,str2);//将str1转化为无格式的字符串,并把其放在str2中
printf("%d",str2.n);
printf("%s",str2.str);
function2(str2); //判断回文串,并求出其位置放在a,b之中
function3(); //输出
}
int function1(struct String string1,struct String string2)//通过判断字符是否为字母,放入str2中
{
int a=0, b=0;
for(a=0; a<string1.n; a++)//判断是否为字母
{
if(isalpha(string1.str[a]))
{
string2.str[b] = toupper(string1.str[a]);//强制转化为大写字母利于比较
p[b++] = a;//记录这个字符在原字符串中的位置
}
}
return strlen(string2.str);
}
void function2(struct String string)//用循环判断出回文串
{
int i,j,k,n=0;//n代表回文串的长度
int bool=1;//代表是不是回文串
for(i=0; i<string.n; i++)
{
for(j=i; j<string.n; j++)
{
bool = 1;
for(k=i; k<(i+j)/2; k++)
{
if(string.str[i+k] != string.str[j-k])
bool = 0;
}
if(bool==1 && j-i+1 > n)
{
n = j-i+1;
a = p[i];
b = p[j];
}
}
}
}
void function3()//简单的输出
{
int i;
printf("最大回文串为:");
for(i=a; i<=b; i++)
{
printf("%c",str1.str[i]);
}
printf("长度为:%d\n",a-b);
}
这段代码不对,运行不出来。
3.高精度算法
首先要定义一个结构体,其中有一个int数组和一个int变量代表其长度。
#define MAXN 1000 + 10
struct huge
{
int a[MAXN];
int len;
};
高精度加法:
huge jiafa(huge a, huge b)
{
int i, L;
huge tmp;
L=max(a.len, b.len);
for(i = 1; i <= L + 1; i++)
tmp.a[i] = 0;
for(i = 1;i <= L; i++)
{
tmp.a[i] += a.a[i] + b.a[i];
tmp.a[i+1] += tmp.a[i] / 10;
tmp.a[i] = tmp.a[i]%10;
}
tmp.len = L;
if(tmp.a[L + 1]) tmp.len++;
return tmp;
高精度减法:
与高精度加法原理相同,最方便的记法就是背过。。
huge jianfa(huge a, huge b)
{
int i, L;
huge tmp;
L=max(a.len, b.len);
for(i = 1; i <= L + 1; i++)
tmp.a[i] = 0;
for(i = 1;i <= L; i++)
{
tmp.a[i] += a.a[i] - b.a[i];
if(tmp.a[i] < 0)
{
tmp.a[i] += 10;
a.a[i + 1]--;
}
tmp.len = L;
while(!tmp.a[tmp.len])
tmp.len--;
return tmp;
}
高精度乘法:
huge chengfa(huge a, huge b)
{
huge c;
int L = a.len + b.len;
for(int i = 1;i <= L;i++)
c.a[i] = 0;
for(int i = 1;i < a.len; i++)
for(int j = 1;j <=b.len; j++)
{
c.a[i + j -1] += a.a[i] * b.a[j];
if(c.a[i + j -1] >= 10)
{
c.a[i + j] += c.a[i + j -1] / 10;
c.[i+j-1] %= 10;
}
}
c.len = L;
while((c.len > 1) && (!c.a[c.len]))
c.len--;
return c;
}
高精度除法是四则运算中最难实现的一个,与平常手工运算不再完全一样,因为模拟手工算法太慢了。
高精度四则运算还可以用类的方法封装起来,重载加减乘除运算符,以便随时使用。
4.排列组合
递归实现比较简单,但开销很大,有非递归实现,但不容易理解,同时有不止一种的递归方法,有时间再更。
全排列(A)
将数组看为一个集合,将集合分为两部分:0~a和s~b,其中0~a表示已经选出来的元素,而a~b表示还没有选择的元素。
把数组分为两部分,前面0到a表示已经选出来的元素,a到b表示没有选择的元素。
#define LEN 100
int array[LEN];
void quanPaiLie(int array[], int a, int b) //b为数组长度,a从头开始。
{
int i;
if(a > b)
{
myPrint();//这里就开始输出
}
else
{
for(i = a; i <= b; i++)
{
swap(array, a, i);
quanPaiLie(array, a + 1, b);
swap(array, a, i);
}
}
}
void swap(int * list, int i, int j)
{
int tmp = list[i];
list[i] = list[j];
list[j] = tmp;
}
void myPrint()//仅仅是一个打印函数
{
for(int i = 0; i < LEN; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
组合(C)
每一次从集合中选出一个元素,然后对剩余的集合(n-1)进行一次k-1组合(最后只有两个元素的组合是很好想的),到最后输出。
void zuHe(int array[], int n, int k)
{
if(k == 0)
{
myPrint();
return;
}
for(int i = n; i >= k; i--)
{
subset[k-1] = array[i-1];
if(k > 1)
{
zuHe(array, i-1, k-1);
}
else
{
myPrint();
}
}
}
排序与检索
在stdlib.h中有排序函数qsort,使用库函数排序的代码量不比冒泡排序法小,但速度却快很多。t
冒泡排序