每日算法之1
1.之输出数组中重复出现的数字
//问题描述:给出n个长度的数组,其中含有随机数,输出其中重复出现的数字,并计算其出现的次数
/*
算法分析:数据结构,n个随机数的数组,含int重复数字,int次数的结构体数组
过程分析,首先进行一次快排,然后对数组进行遍历扫描,每次判断当前元素与下一个元素是否相等
如果相等,则存入该数,并进行计数,直到不相等,存入计数值;重置,开始下一次扫描
直到与最后一个数判断完毕;;将该数组输出
*/
//算法实现:
void quickSort(int a[],int l,int r){
int temp;
int i=l,j=r;
if(l<r){
temp=a[l];
while(i<j){
//反向扫描
while(a[j]>=temp && i<j) --j;
if(i<j) a[i++]=a[j];
//正向扫描
while(a[i]<=temp && i<j) ++i;
if(i<j) a[j--]=a[i];
}
a[i]=temp;//i、j重合退出循环,将中轴值填上,并拿到新的中轴位置
quickSort(a,l,i-1);
quickSort(a,i+1,r);
}
}
void generateRandom(int a[],int n,int Max){//定义一个随机数生成函数
srand((unsigned)time(NULL));
for(int i=0;i<n;++i){
a[i]=rand()%MAX;
}
}
struct repeatNum_s{
int reNum;
int reCount;
};
void findRepeatNum(int a[],int n){
if(n<2 || a==NULL) cout<<"error";
repeatNum_s res[n];//自定义一个存入重复数字的数组
int count;
int i=0;
int j=0;
while(i<n-1){//注意边界条件为0~n-2
if(a[i]==a[i+1]){
count=1;
while(a[i]==a[i+1] && i<n-1){
++count;
++i;
}
res[j].reNum=a[i];
res[j].reCount=count;
++j;
}
++i;
}
if(j==0) cout<<"没有重复数字!"<<endl;
for(int k=0;k<j;++k)
cout<<"重复的数字为:"<<res[k].reNum<<" 重复次数为:"<<res[k].reCount<<"\n";
}
//时间复杂度分析:快排时间复杂度为O(nlogn)+查找时间复杂度为:O(n),所以最终为O(n)
//,空间复杂度为O(n)
//测试用例:
#include <iostream>
#include <cmath>
#include <ctime>
#include <cstdlib>
using namespace std;
int main(){
int MAX=1000;
int a[MAX];
generateRandom(a,1000,MAX);
quickSort(a,0,MAX-1);
for(int i=0;i<MAX;++i)
cout<<a[i]<<"\t";
cout<<endl;
findRepeatNum(a,1000);
}
2.之查找行列非递减二维数组指定元素
//问题描述:给定一个二维数组,其行,列元素非递增排列,要求查找指定元素k是否存在?
/*
算法分析:从右往左扫描,如果该值大于k,则将列减一剔除;如果相等,则返回true,退出;如果小于
则行加一剔除;如此循环直到最后一行,第一列
*/
//算法实现:
void find(const int *matrix,int rows,int columns,int k){//使用二维数组行和列传参的形式!!
int row=0;
int col=columns-1;
if(matrix!=NULL && rows>0 && columns>0){//注意此法不使用new创建的二维数组,由于不连续!
while(row<rows && col>=0){
if(matrix[row*columns+col]==k){
cout<<"元素k="<<k<<"所在行:"<<row+1<<" 所在列:"<<col+1<<endl;
for(int i=row+1;i<rows;++i){//需要判断是否该列含有多个相等值
if(matrix[i*columns+col]==k)
cout<<"元素k="<<k<<"所在行:"<<i+1<<" 所在列:"<<col+1<<endl;
else break;
}
--col;//判断完毕才将此列剔除
}
else if(matrix[row*columns+col]>k){
--col;
}
else {
++row;
}
}
}else
cout<<"not found!!\n";
}
//复杂度:最坏情况下时间复杂度为O(col || row),线性时间
int main(){
//测试用例
int a[][4]={{2,2,2,2},{2,2,2,2},{2,2,2,2},{2,2,2,2}};
find((int*)a,4,4,2); //注意二维数组传入数组指针
}
3.之C++字符串
//1、C++规定字符串一般结尾都会自动添加一个结束标志:‘\0’,所以常量字符串的长度一般需要+1
//2、C++一般将常量字符串单独放于一个内存区域
#include <iostream>
using namespace std;
int main()
{
/*3、当一个字符串常量出现于表达式中时,它的值是个指向第一个字符的指针常量。编译器把这个指定字符的一份copy存储在内存的某个位置,
并存储一个指向第一个字符的指针。*/
cout << *"xyz" << endl;//x
cout << *"xyz"+1 << endl;//y的ascll码
cout << "xyz"[2] << endl;//z
//cout << *("xyz"+4) << endl;
cout << "xyz"+1<< endl;//yz
/*======等价于======*/
cout << "/*======等价于======*/"<< endl;
const char *p="xyz";//由于是常量字符串,必须使用一个指针常量进行接受,否则会出现警告
cout << *p << endl;//x
cout << *p+1 << endl;//y的ascll码
cout << char(*p+1) << endl;//y
cout << p[2] << endl;//z
cout << p+1<< endl;//yz
return 0;
}
//原因在于:C++对cout<<输出字符串数组时对运算符进行了重载,输出时直到'\0'字符为止;;
//4、所以如果是常量字符串,或者指向字符串常量的指针,或者string类型的,输出时都会直接输出整个字符串;
//这一点与整型数组是不同的
//5、对于相同的字符串常量,它的内存地址是相同的,一般都会存于静态全局data区域
//如:p1和p2指向相同的内存地址
char *p1="hello world";
char *p2="hello world";
//既然常量字符串实际是一个地址,所以定义一个字符数组时可以直接赋值初始化,但是非定义时不能够进行直接
//将一个常量字符串赋给一个数组,类型都不一致,此外
//千万记住常量字符串实际是一个常量,意味着无法通过指针修改常量值
const char *p="hello world!";
*p="yang";//试图修改而出错
4.之字符串替换空格
//问题描述:输入一个字符串数组"we are happy.",要求将其中的空格进行替换为%20
/*
算法分析:思路一:正向扫描整个字符串,遇到空格,则将其后的字符串后移2个字符,并将空格替换为%20;
直到遇到结束字符'\0';; 缺点,虽然不需要借助辅助空间,但是时间复杂度为O(n^2)
思路二:先正向扫描一遍,统计所有空格的数量,算好新字符串的长度,使用两个指针p1,p2
指向原来的字符串和新的字符串末尾,移动p1指针并将字符复制在p2处,碰到空格,
则直接赋值%20;直到两个指针相等
优点:时间复杂度为O(n)线性的时间
*/
//算法实现:
void replace_space(char c[]){
if(c==NULL){
cout<<"error!"<<endl;
return;
}
int spaceNum=0;
int newLength;
int oldLength;
int i;
for(i=0;c[i]!='\0';++i){
if(c[i]==' ')
++spaceNum;
}
oldLength=i+1;
newLength=spaceNum*2+oldLength;
char *p1=&c[oldLength-1];
char *p2=&c[newLength-1];
while(p1!=p2){
while(*p1!=' ' && p1!=p2){
*p2=*p1;
--p1;
--p2;
}
*p2='0';--p2;
*p2='2';--p2;
*p2='%';--p2;
--p1;
}
cout<<c;
}
/*
!!注意:数组名a和&a,及数组指针和指针数组的区别
假设:
定义一个数组名int a[100];
a等价于int*,表示指向数组首元素的地址的一个指针;;&a的类型则相当于int**,是所谓指向整个数组的指针,是数组元素类型的二级指针
注意:&a 和a 的值一样,但代表的意义不同
a + 1是相当于a + 1 * sizeof(int)
&a + 1 * sizeof(a)的,所以会偏移一个数组长度
数组指针和指针数组的区别:
int* p1[10];//表示指针数组
int (*p1)[10];//表示数组指针
int **p1=new int*[10];//表示二级指针
首先一级指针初始化是地址赋值,但是二级指针赋值是使用指针赋值而非地址
*/