您的位置:首页 > 编程语言

学习编程艺术的心得、知识总结1(长期更新)

2015-06-16 11:59 337 查看
本文章用于记录阅读七月大神的博客编程艺术的心得、知识,在看了两三章节的过程中,每次都是先看思路,自己编写调试后和他的代码对比,在自己编写过程中,发现了自己对编程知识方法的许多错误使用,同时学习到了简洁的编程方式。在整个阅读过程,想到遇到什么都记录一下,在反思中学习进步!!

七月的编程艺术github版网址:https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/Readme.md。

一、字符串包含

知识点:

1、对string类型的变量a使用sort排序:
sort(a.begin(),a.end());
2、算法的位运算实现,比较经典,对位操作比较陌生,代码记录在此,方便随时学习。
// “最好的方法”,时间复杂度O(n + m),空间复杂度O(1)
bool StringContain(string &a,string &b)
{
int hash = 0;
for (int i = 0; i < a.length(); ++i)
{
hash |= (1 << (a[i] - 'A'));
}
for (int i = 0; i < b.length(); ++i)
{
if ((hash & (1 << (b[i] - 'A'))) == 0)
{
return false;
}
}
return true;
}


二、字符串转换为整数

按照最初的想法,直接读取字符串中的一个,然后转为数字,然后以num=num*10+m更新即可,自己写了如下的函数来实现。

int StrToInt1(const char *str)
{
int num=0;
while(str)
{
int m=*str-'0';
num=num*10+m;
str++;
}
return num;
}


结果发现运行出错。原因是,str指针已经被赋值了,在str++的过程中,str始终不为一个空指针,while循环的判断条件一直成立,因此出错。此时,参考博客,将while判断改为*str==0即可,即指针指向的值为0。

知识点:

设置int型数的最大值和最小值,用位运算实现如下:

static const int MAX_INT = (int)((unsigned)~0 >> 1);
static const int MIN_INT = -(int)((unsigned)~0 >> 1) - 1;


三、回文判断

当实现函数的参数形式如下为string类型时:

bool palind(string *s, int n)
此时直接看成数组,设定i从0到s.length()/2即可。

当实现函数的参数形式如下时:

bool palind(const char *s, int n)


字符串用字符指针s来表示的,此时用两个指针指示首尾,依次改变指针指向进行比较。此时对两个指示指针赋值如下:

char *first,*last;
first=s;
last=s+n-1;
结果出现了如下错误:

error C2440: '=' : cannot convert from 'const char *' to 'char *'

在网上查找解决办法是进行强制转化,修改如下:

char *first=(char *)s;
char* last=(char *)(s+n-1);


四、字符串全排列

输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c 所能排列出来的所有字符串abc、acb、bac、bca、cab 和 cba。

在看到题目后,想到以前做过的一个深度优先遍历题目,此题也可使用此思路,从a开始查找到b再到底部c返回一条路径,然后向上返回,查找到c再到b。然后返回到最开始,从b开始查找。

下面是基于此深度搜索的实现代码:
#include<iostream>
#include<string>
using namespace std;

string a="abcd";
int lu[4];  //保存路径
int rest[4];//剩余未访问的字符
int j=0,restone_temp=-1; //j为路径计数,restone_temp为剩余一个未访问时,此未访问节点的下标,
<span style="white-space:pre">		</span>//因为剩余一个未访问时,路径已经确定,此设置方便返回到上一步选择的节点
bool restone()  //判断是否剩余一个未访问
{
int count=0;
int k=-1;
for(int i=0;i<a.length();i++)
{
if(rest[i]==1)
{
count++;
k=i;
}

}
if(count==1)
{
restone_temp=k;  //记录还有一个未访问时,此节点的下标
return true;
}
else
return false;
}
void strsort()
{
if(restone())   //剩余一个即确定了组合方式,打印结果
{
for(int i=0;i<a.length()-1;i++)
cout<<a[lu[i]];
cout<<a[restone_temp];
cout<<endl;
return;
}

for(int i=0;i<a.length();i++)
{
if(rest[i])
{
rest[i]=0;
lu[j++]=i;
strsort();  //递归

rest[i]=1;  //返回上一步选择的节点,如果访问到最后一个才返回,需要向上返回两次,
j--;<span style="white-space:pre">	</span>//但返回两次时,向上返回的节点难以确定
}
}
}

int main()
{
for(int i=0;i<a.length();i++)
rest[i]=1;
strsort();
return 0;
}
(⊙﹏⊙)汗。。检查了一下代码,发现上述的代码判断剩余一个未访问的操作完全多余,在递归过程中,在向上返回一次时,因为没有其他路径可走,又会自动向上返回一次,因此上述代码严重冗余。对深度搜索的递归实现认识还不足,需要进一步学习。改进的代码如下:

#include<iostream>
#include<string>
using namespace std;

string a="abcd";
int lu[4];
int rest[4];
int j=0;

bool norest()  //判断是否所有点已经被访问
{
for(int i=0;i<a.length();i++)
{
if(rest[i]==1)
return false;
}
return true;
}

void strsort()
{
if(norest())  //所有点被访问,打印路径
{
for(int i=0;i<a.length();i++)
cout<<a[lu[i]];
cout<<endl;
return;
}
for(int i=0;i<a.length();i++)
{
if(rest[i])
{
rest[i]=0;
lu[j++]=i;
strsort();

rest[i]=1;
j--;
}
}
}

int main()
{
for(int i=0;i<a.length();i++)
rest[i]=1;
strsort();

return 0;
}

而作者文中的递归实现,使用了交换操作,勉强理解了,感觉实现有点复杂。

第二种方法是基于字典序排列,即使用了STL中的next_permutation算法思想来实现的,再往后的实现中,可直接循环调用此函数来实现全排列,可大大简化代码,下面用此函数实现字符串的全排列如下:

#include<iostream>
#include<string>
#include <algorithm>
using namespace std;

int main()
{
char ch[200];
cin>>ch;
sort(ch,ch+strlen(ch));
do{
cout<<ch<<endl;
}while(next_permutation(ch, ch+strlen(ch)));

return 0;
}
思考题中,已知字符串里的字符是互不相同的,现在任意组合,比如ab,则输出aa,ab,ba,bb,作者提供的解决方法非常巧妙,也是基于递归实现的,记录在此:

#include<iostream>
#include<string>
#include <algorithm>
using namespace std;

void perm(char* result, char *str, int size, int resPos)
{
if(resPos == size)
printf("%s\n", result);
else
{
for(int i = 0; i < size; ++i)
{
result[resPos] = str[i];
perm(result, str, size, resPos + 1);
}
}
}

int main()
{
char ch[200];
cin>>ch;
sort(ch,ch+strlen(ch));
char result[200]={0};
perm(result,ch,strlen(ch),0);

return 0;
}


五、最小的K个数

其中使用堆来维护K个数的集合还没实现,因为一直以来对堆就很陌生,所以没实现。下面是直接根据快速选择思路实现的代码:

#include<iostream>
#include<algorithm>
using namespace std;

int median3(int a[],int first,int last)
{
int midium=(first+last)/2;
if(a[first]>a[midium]&&a[first]>a[last])
{
if(a[midium]>a[last])
return midium;
else
return last;
}
if(a[first]<a[midium]&&a[first]<a[last])
{
if(a[midium]<a[last])
return midium;
else
return last;
}
return first;
}

void quickselect(int s[],int first,int last,int k)
{
int m=0,n=0;
int piv=0;
int s1[100],s2[100];

piv=median3(s,first,last);
for(int i=0;i<=last;i++)
{
if(s[i]>=s[piv])
{
s2[n++]=s[i];
}
else
s1[m++]=s[i];
}
if(k>m)
{
for(int j=first;j<n;j++)
cout<<s1[j]<<" ";
quickselect(s2,0,n-1,k-m);
}
else if(k<m)
quickselect(s1,0,m-1,k);
else
{
for(int j=first;j<m;j++)
cout<<s1[j]<<" ";
cout<<endl;
}
}

int main()
{
int arr[100]={462359,161047,2346,716099,189159,264108,905891,115240,473403,221514,
723403,684465,257361,34611,535903,478932,345173,117798,693203,593953};
quickselect(arr,0,19,10);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: