里面挺多细节的东西,每隔一段时间还要来复习一下。
1.求链表中的倒数第k个节点
用两个指针,第一个指针先走k-1步。第二个指针从头开始走,两个指针一起走,这样当第一个指针走向尾节点时第二个指针指向的就是倒数第k个节点。
代码安全性:
ListNode* FingKthToTail(ListNode* pListHead,unsigned int k)
{
if(pListHead==NULL)
return NULL;
ListNode *pAhead=pListHead;
ListNode *pBehind=NULL;
for(unsigned int i=0;i<k-1;i++)
{
pAhead=pAhead->m_pNext;
}
pBehind=pListHead;
while(pAhead->m_pNext!=NULL)
{
pAhead=pAhead->m_pNext;
pBehind=pBehind->m_pNext;
}
return pBehind;
}
2.将字符串转化成整数:
int StrToInt(char* string)
{
int number=0;
while(*string!=0)
{
number=number*10+*string-'0';
++string;
}
return number;
}
sizeof(空类型\空类型+构造&析构函数)值为1。
sizeof(虚函数)值为:32位指针为4,64位指针为8。
3.构造函数:
class A
{
private:
int value;
public:
A(int n){value=n;}
A(const A& other){value=other.value;}//就这样写才可以实现复制构造
}
int main()
{
A a=10;
A b=a;//复制构造
}
4.如何实现一个赋值构造函数:
class CMyString
{
public:
CMyString(char* pData=NULL);
CMyString(const CMyString& str);
~CMyString(void);
private:
char* m_pData;
};
//是否把返回值的类型声明为该类型的引用
CMyString& CMyString::operator=(const CMyString &str)
{
if(this==&str)
return *this;//需要判断两者是不是属于同一个实例,如果是同一个那么释放的话就会释放掉再也找不回来
delete []m_pData;//在分配新内存前要释放自身已有的空间,否则程序将出现内存泄露。
m_pData=NULL;
m_pData=new char[strlen(str.m_pData)+1];
strcpy(m_pData,str.m_pData);
return *this;//是否返回实例自身的引用(*this),只有返回一个引用,才可以允许连续赋值。
}
CMyString& CMyString::operator=(const CMyString &str)
{
if(this!=&str)
{
CMyString strTemp(str);
char* pTemp=strTemp.m_pData;
strTemp.m_pData=m_pData;
m_pData=pTemp;//使用实例再与原实例交换。
}
return *this;
}
int GetSize(int data[])//在这里数组会退化为指针,返回的值是一个指针,为4字节。
{
return sizeof(data);
}
int _tmain(int argc,_TCHAR* argv[])
{
int data1[]={1,2,3,4,5};
int size1=GetSize(data1);
}
当你声明一个数组的时候
5.Singleton模式
public sealed class Singleton4
{
private Singleton4()
{
}
private static Sinleton4 instance=new Singleton4();//静态构造函数能够确保只调用一次
public static Singleton4 Instance
{
get
{
return instance;
}
}
}
6.规律二维数组中的数字查找
bool Find(char* matrix,int rows,int columns,int number)
{
bool found=false;
if(columns>0&&rows>0&&matrix!=NULL)
{
int row=0;
int column=columns-1;
while(row<rows&&column>=0){
if(matrix[row*columns+column]==number)
{
found=true;
break;
}
else if(matrix[row*columns+column]>number)
--column;
else
++row;}
}
return found;
}
7.
char str1[]和char str2[]指向的是不同的地址
char *str3和char *str4指向的是同一个地址
8.替换空格:
while(string[i]!='\0')
{
++originalLength;
if(string[i]==' ')
++numberOfBlank;
++i;
}
We Are Happy改为We%20Are%20Happy
做法是计算出新的长度,然后从末尾开始粘贴过去。
while(){
if(string[indexOfOrigin]==' ')
{
string[indexOfNew--]='0';
string[indexOfNew--]='2';
string[indexOfNew--]='%';
}
else
{
string[indexOfNew--]=string[indexOfOrigin];
}
--indexOfOrigin;
}
9.反向打印列表
链表:
struct ListNode
{
int m_nkey;
ListNode* m_pNext;
};
a.用栈存储输出
void PrintListReversingly(ListNode *pHead)
{
std::stack<ListNode*> nodes;
ListNode* pNode=pHead;
while(pNode!=NULL)
{
nodes.push(pNode);
pNode=pNode->m_pNext;
}
while(!nodes.empty())
{
pNode=nodes.top();
printf("%d\t",pNode->m_nValue);
nodes.pop();
}
}
//递归在本质上就是一个栈结构
b.递归调用输出
void PrintListReversingly(ListNode* pHead)
{
if(pHead!=NULL)
{
if(pHead->m_pNext!=NULL)
{
PrintListReversingly(pHead->m_pNext);
}
printf("%d\t",pHead->m_nValue);
}
}
c.链表反向
10.二叉树一个特例是二叉搜索树,其他的特例是堆和红黑树。
已知两个搜索顺序(前序和中序),求重建的二叉树。
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
};
????????
11.双栈实现队列
template<typename T>void CQueue<T>::appendTail(const T& element){stack1.push(element);}
template<typename T>T CQueue<T>::deleteHead(){
if(stack2.size()<=0)
{
while(stack1.size()>0)
{
T& data=stack1.top();
stack1.pop();
stack2.push(data);
}
}
if(stack2.size()==0)
throw new exception("queue is empty");
T head=stack2.top();
stack2.pop();
return head;
}
12.很多算法都可以使用递归和循环两种不同的方式进行实现,基于递归的代码会比较简洁,但性能不如基于循环的实现方式。
位运算只有与、或、异或、左移和右移5种位运算。
查找无外乎顺序查找、二分查找、哈希表查找和二叉排序树查找。
哈希表的优点在于能够利用O(1)时间查找某一元素,是效率最高的查找方式。但其缺点是需要额外的空间来实现哈希表,是空间换时间的方式。类似题目:第一个只出现一次的字符就是使用哈希表来查找。与二叉排序树查找算法对应的是二叉搜索树。
计数排序时间复杂度为O(n),空间复杂度也为O(n),大致思路是两个数组,第一个循环为一个数组timesOfAge[age]用来存每个age的次数,也就是int age=ages[i];++timesOfAge[age];第二个循环为双重循环,正式对age排序,ages[index]=i;++index;第一层循环为所有年龄,第二层循环为timesOfAge[i]。
void SortAges(int ages[],int length)
{
if(ages==NULL||length<=0)
return;
const int oldestAge=99;
int timesOfAge[oldestAge+1];
for(int i=0;i<oldestAge;i++)
timesOfAge[i]=0;
for(int i=0;i<length;i++)
{
int age=ages[i];
if(age<0||age>oldestAge)
throw new std::exception("age out of range");
++timesOfAge[i];
}
for(int i=0;i<oldestAge;i++)
{
for(int j=0;j<timesOfAge[i];j++)
{
ages[index]=i;
++index;
}
}
}
13.我们注意到旋转之后的数组实际上可以划分的两个排序的子数组,数组在一定程度上是排序的,因此我们可以试着用二分查找法的思路寻找最小的元素。
int Min(int *numbers,int length)
{
if(numbers==NULL||length<=0)
throw new std::exception("Invalid parameter");
int index1=0;
int index2=length-1;
int indexMid=index1;
while(numbers[index1]>=numbers[index2])
{
/*if(index2-index1==1)
{
indexMid=index2;
break;
}*/
/*
if(numbers[index1]==numbers[index2]&&numbers[indexMid]==numbers[index1])
int result=numbers[index1];
for(int i=index+1;i<=index2;i++)
{
if(result>numbers[i])
result=numbers[i];
}*/
indexMid=(index1+index2)/2;
if(numbers[indexMid]>=numbers[index1])
index1=indexMid;
else if(numbers[indexMid]<=numbers[index2])
index2=indexMid;
}
return numbers[indexMid];
}
14.
int AddFromlToN_Recursive(int n)
{
return n<=0?0:n+AddFromlToN_Recursive(n-1);
}
但由于是函数调用自身,而函数调用是有时间和空间消耗的:每一次函数调用,都需要在内存中分配空间以保存参数、返回地址和临时变量,而往栈里压入数据和弹出数据都需要时间,另外递归中很多计算是重复的。
递归还有可能引起调用栈溢出的问题。从上往下的递归计算会有很多重复的节点,一个解决方法是存储中间项,但其实更简单的方法是从下往上计算。且时间复杂度为O(n)。
常见:
long long Fibonacci(unsigned int n)
{
if(n<=0)
return 0;
if(n==1)
return 1;
return Fibonacci(n-1)+Fibonacci(n-2);
}
从下往上思路:
long long Fibonacci(unsigned int n)
{
int result[2]={0,1};
if(n<2)
return result[n];
long long fibNMinusOne=1;
long long fibNMinusTwo=0;
long long fibN=0;
for(unsigned int i=2;i<=n;i++)
{
fibN=fibNMinusOne+fibNMinusTwo;
fibNMinusTwo=fibNMinusOne;
fibNMinusOne=fibN;
}
return fibN;
}
一只青蛙一次可以跳1级台阶也可以跳2级台阶,求该青蛙跳上一个n级的台阶总共有多少种跳法。
公式:f(n)=f(n-1)+f(n-2);
15.
n=(n-1)&n;//该公式用于求二进制数据中1的个数。
return (x&(x-1)==0)&&(x!=0)//该公式说明x为2的幂,因为所有位上只有一个数为1。
位与相当于是取余
a&0x1==1(a为奇数)
a&0x1==0(a为偶数)
右移>>,意为除以2。
将a的第k位清零:a=a&~(1<<k)
将a的第k位置一:a=a|(1<<k)
x的相反数是(~x+1)
不用temp来交换两个整数:
void swap(int x,int y)
{
x^=y;
y^=x;
x^=y;
}//^被称为位异或,常用于交换。
清零用&,置一用|。
求int型中1的个数:
int NumberOf1(int n)
{
int count=0;
while(n)
{
++count;
n=(n-1)&n;
}
reurn count;
}
16.常用数组前后交换法:
void ReorderOddEvent(int *pData,int length,bool (*func)(int))//注意这里:bool isEven(int n){return (n&1)==0;}
{
if(pData=NULL||length=0)
return;
int *pBegin=pData;
int *pEnd=pData+length-1;
while(pBegin<pEnd)
{
while(*pBegin&(0x1)!=0&&pBegin<pEnd)
{
pBegin++;
}
while(*pEnd&(0x1)==0&&pBegin<pEnd)
{
pEnd--;
}
if(pBegin<pEnd)
{
int temp=*pBegin;
*pBegin=*pEnd;
*pEnd=temp;
}
}
}
17.链表合并
ListNode* Merge(ListNode* pHead1,ListNode* pHead2)
{
if(pHead1==NULL)
return pHead2;
if(pHead2==NULL)
return pHead1;
ListNode* pMergedHead=NULL;
if(pHead1->m_nValue>pHead2->m_nValue)
{
pMergedHead=pHead2;
pMergedHead->m_pNext=Merge(pHead1,pHead2->m_pNext);
}
else
{
pMergedHead=pHead1;
pMergedHead->m_pNext=Merge(pHead1->m_pNext,pHead2);//注意此处的递归
}//使用递归的时候不需要走循环,pMergedHead->m_pNext就可以不断递归
return pMergedHead;
}
18.链表翻转
ListNode* Reverse(ListNode* pHead)
{
ListNode* pReverseHead=NULL;
ListNode* pNode=pHead;
ListNode* pPrev=NULL;
while(pNode!=NULL)
{
pNext=pNode->m_pNext;
if(pNode->m_pNext==NULL)
pReverseHead=pNode;
pNode->m_pNext=pPrev;
pPrev=pNode;
pNode=pNext;
}
return pReverseHead;
}
pNext=pNode->m_pNext=pPrev
pPrev=pNode=pNext
19.二叉树的镜像
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
};
树的子节点翻转:
void MirrorRecursively(BinaryTreeNode *pNode)
{
if(pNode==NULL||pNode->m_pLeft==NULL&&pNode->m_pRight)
return;
BinaryTreeNode* temp=pNode->m_pLeft;
pNode->m_pLeft=pNode->m_pRight;
pNode->m_pRight=temp;
if(pNode->m_pLeft)
MirrorRecursively(pNode->m_pLeft);
if(pNode->m_pRight)
MirrorRecursively(pNode->m_pRight);
}
//递归也就是先确定跳转的条件以及要做的操作,然后进行递归,不断实现定义的操作。递归的条件由pNode转变为pNode->m_pLeft和pNode->m_pRight。
20.树的子结构
21.很多时候我们需要一个辅助结构来帮助我们快速进行算法。
找出当前栈中的最小值,我们需要一个辅助栈,这个栈与数据栈共同更新,只是存储侧重点不一样。
22.广度优先遍历一个有向图是基于队列实现的,树是图的一种特殊退化形式,从上到下按层遍历二叉树本质上来说就是广度优先遍历二叉树。需要基于队列来实现。
deque使用,用于通过第一个节点去不断往深处遍历。
void PrintFromTopToBottom(BinaryTreeNode* pTreeRoot)
{
if(!pTreeRoot)
return;
std::deque<BinaryTreeNode*> dequeTreeNode;
dequeTreeNode.push_back(pTreeRoot);
while(dequeueTreeNode.size())
{
BinaryTreeNode *pNode=dequeueTreeNode.front();
dequeueTreeNode.pop_front();
printf("%d",pNode->m_nValue);
if(pNode->m_pLeft)//要理解队列与按层遍历的关系,中左右(左)左右(右)左右
dequeueTreeNode.push_back(pNode->m_pLeft);
if(pNode->m_pRight)
dequeueTreeNode.push_back(pNode->m_pRight);
}
}
std::deque<BinaryTreeNode*> dequeBinaryTree;
std::vector<int> path;
std::vector<int>::iterator iter=path.begin();
for(;iter!=path.end();++iter)
printf("%d\t",*iter);
23.复杂链表的复制:
struct ComplexListNode
{
int m_nValue;
ComlpexListNode* m_pNext;
ComplexListNode* m_pSibling;
};
void CloneNodes(ComplexListNode* pHead)//先复制链表,再将复制的节点与原链表相连
{
ComplexListNode* pNode=pHead;
while(pNode!=NULL)
{
ComplexListNode* pCloneNode=new ComplexListNode();
pCloneNode->m_nValue=pNode->m_nValue;
pCloneNode->m_pNext=pNode->m_pNext;
pCloneNode->m_pSibling=NULL;
pNode->m_pNext=pCloneNode;
pNode=pCloneNode->m_pNext;
}
}
void ConnectSibling(ComplexListNode* pHead)//进行Sibling的连接
{
ComplexListNode* pNode=pHead;
while(pNode!=NULL)
{
ComplexListNode* pCloneNode=pNode->m_pNext;
if(pNode->m_pSibling)
{
pCloneNode->m_pSibling=pNode->m_pSibling->m_pNext;
}
pNode=pCloneNode->m_pNext;
}
}
ComlpexListNode* ReconnectNodes(ComplexListNode* pHead)
{
ComplexListNode* pNode=pHead;
ComplexListNode* pCloneNode=NULL;
ComplexListNode* pCloneHead=NULL;
if(pNode!=NULL)//先设置好每个链表的头结点指针
{
pCloneNode=pCloneHead=pNode->m_pNext;
pNode->m_pNext=pCloneNode->m_pNext;
pNode=pNode->m_pNext;//也就是链表指针前进两步
}
while(pNode!=NULL)//设置每个链表的下一步,先设置pCloneNode再设置pNode。
{
pCloneNode->m_pNext=pNode->m_pNext;
pCloneNode=pCloneNode->m_pNext;
pNode->m_pNext=pCloneNode->m_pNext;
pNode=pNode->m_pNext;
}
return pCloneHead;
}
24.二叉搜索树与双向链表
???????
25.字符串的排列
将字符串分为第一个字符与其他所有字符,将第一个字符与其他所有字符中的字符交换并进行递归,就可以实现全排列的情况,也就是next_permutation
void permutation(char *pStr)
{
if(str==NULL)
return;
permutation(pStr,pStr);
}
void permutation(char* str,char* begin)
{
char temp=*str;
*str=*begin;
*begin=temp;
permutation(str,begin+1);
temp=*str;
*str=*begin;
*begin=temp;
}//也就是最外层循环是a和bcd换,第二层循环是b和cd换,第三层循环是c和d换。
全排列对于求一些特定排序的数组值特别有用,我们可以先列出所有的排列然后选取特定的符合条件的排列。这里涉及到数值数组的全排序,还有去重的组合排列。
八皇后:
void Permutation()
{
const int queens=8;
int Column[queens]={0};
for(int i=0;i<queens;i++)
Column[i]=i;
permutation(int Column,int length,0);
}
void Permutation(int Column[],int length,int index)
{
if(index==length)
{
if(Check(Column,length))
{
++g_number;
PrintQueen(Column,length);
}
}
else
{
for(int i=index;i<length;i++)
{
int temp=Column[i];
Column[i]=Column[index];
Column[index]=temp;
Permutation(Column,length,index+1);
int temp=Column[i];
Column[i]=Column[index];
Column[index]=temp;
}
}
}
bool Check(int Column[],int length)
{
for(int i=0;i<length;i++)
{
for(int j=i+1;j<length;j++)
{
if((i-j==Column[i]-Column[j])||(j-i==Column[i]-Column[j]))
return false;
}
}
return true;
}
注意:
用void swap(int *a,int *b){int m=*a;*a=*b;*b=m;}时
调用方法为swap(&list[k],&list[i])
26.partition()函数的使用O(n)
int Partition(int number[],int length,int start,int end)
{
int i=start;
int j=end;
int pivot=number[left];
while(i<j)
{
while((i<j)&&number[i]<number[pivot])
i++;
while((i<j)&&number[j]>number[pivot])
j--;
if(i<j)
{
int temp=number[i];
number[i]=number[j];
number[j]=temp;
}
}
int temp=number[i];
number[i]=number[left];
number[left]=temp;
return i;
}
使用1:
求数组中出现次数超过一半的数字:
特点在于,如果对该数组进行排序这个数字一定位于排序后数组的中位数上。
int SearchMiddle(int number[],int length)
{
int middle=length>>1;
int start=0;
int end=length-1;
int index=Partition(number,length,start,end);
while(index!=middle)
{
if(index>middle)
{
end=index-1;
index=Partition(number,length,start,end);
}
else
{
start=index+1;
index=Partition(number,length,start,end);
}
}
return number[middle];
}
使用2:
最小的k个数:
void FindLessKth(int number[],int length,int *output,int k)
{
int start=0;
int end=length-1;
int index=Partition(number,int length,int start,int end);
while(index!=k-1)
{
if(index<k-1)
{
start=index+1;
index=Partition(number,int length,int start,int end);
}
else
{
end=index-1;
index=Partition(number,int length,int start,int end);
}
}
for(int i=0;i<k;i++)
{
output[i]=input[i];
}
return output;
}
但是使用Partition方法会修改数组并且不适用于海量数据。
用multiset实现的最大堆解法:insert、begin、erase(iter)
typedef multiset<int,greater<int>> intSet;
typedef multiset<int,greater<int>>::iterator setIterator;
void FindLeastKth(const vector<int>& data,intSet& leastNumbers,int k)
{
leastNumber.clear();
if(k<1||data.size()<k)
return;
vector<int>::const_iterator iter=data.begin();
for(;iter!=data.end();iter++)
{
if((leastNumber.size())<k)
{
leastNumber.insert(*iter);
}
else
{
setIterator iterGreatest=leastNumber.begin();
if(*(leastNumber.begin())>*iter)
{
leastNumber.erase(iterGreatest);
leastNumber.insert(*iter);
}
}
}
}
用priority_queue实现的最大堆与最小堆
最大堆是默认的,通过less实现,构建时只需要priority_queue<int> q;然后push、pop、top、empty等。
最小堆是priority_queue<int,vector<int>,greater<int>> q;
如果是用外部定义的结构体则:
struct Node
{
int x,y;
Node(int a=0,int b=0):x(a),y(b){};
friend bool operator<(const Node &a,const Node &b)
};
inline bool operator<(const Node &a,const Node &b){
if(a.x!=b.x)
return a.x<b.x;
return a.y>b.y;
}
在map和set中insert操作返回的是pair,第一个参数为值,第二个参数为bool函数,用来验证该值是否已经存在于map和set中。在multiset和multimap中返回的应该是该值存在的个数。
还有一个问题在于copy和unique_copy.
copy(coll.begin(),coll.end(),ostream_iterator<string>(cout,"\t"));
unique_copy(coll.begin().coll.end(),ostream_iterator<string>(cout,"\t"));如果重复则只输出一个
27.字符串问题
char a[]="hello";//sizeof(a)=6,sizeof(s)=4。
char *s=a;//这里的s、a、hello中所代表的意思都是h所在的地址,也就是存储字符串首字符的地址。所以用char*来存储会比较省空间一点。
for(int i=0;i<strlen(a);i++)
printf("%c",s[i]);
printf("%c",*s++);
printf("%c",*(s+i));
char** 与char *a[]
char *a[]={"China","French","America","German"};//字符串常量的本质是地址。这里的sizeof(a)为16,因为存储的都是char*指针
char **s=a;
char **s;
*s="hello world";//错误
char **s;
s=(char **)malloc(sizeof(char**));
*s="hello world";//正确
二级指针保存的是指针变量,一级指针保存的是内存单元的地址。虽然都是地址但是类型不一样。
char* g_StrCombine1=new char[g_MaxNumberLength*2+1];
char** strNumbers=(char**)(new int[iLen]);
qsort函数中compare的形参一定是const void*
char *g_Combination1=new char[g_MaxNumberLength*2+1];
char *g_Combination2=new char[g_MaxNumberLength*2+1];
int compare(const void* strNumber1,const void* strNumber2)
{
strcpy(g_Combination1,*(const char**)strNumber1);//也就是char**的数组格式,拿快递的时候突然想通,其实也就是char *(*(const char**)strNumber1)的意思。
strcat(g_Combination1,*(const char**)strNumber2);
strcpy(g_Combination2,*(const char**)strNumber2);
strcat(g_Combination2,*(const char**)strNumber1);
strcmp(g_Combination1,g_Combination2);
}
int Transfer(int *numbers,int length)
{
if(numbers==NULL||length<=0)
return;
char **strNumber=(char **)(new int[length]);//这里需要注意的是char**数组的初始化,也就是二维数组的初始化,
for(int i=0;i<length;i++)
{
strNumber[i]=(char *)new char[g_MaxNumber+1];
sprintf(strNumbers[i],"%d",numbers[i]);//这里只能使用sprintf来控制,普通的赋值会报无法从int转为char*,sprintf_s(buffer,length,format,[argument]);
}
qsort(strNumber,length,sizeof(char*),compare);
for(int i=0;i<length;i++)
{
printf("%s",strNumber[i]);
}
for(int i=0;i<length;i++)
delete[] strNumbers[i];
delete[] strNumbers;
}
int **数组的使用int[10][20]。
int *data与int data[][3]平级。
int **x;
x=(int**)malloc(sizeof(int*)*10);//先分配二级指针再分配一级指针
for(int i=0;i<10;i++)
x[i]=(int*)malloc(sizeof(int)*20);
for(int i=0;i<10;i++)
free(x[i]);//先释放一级指针再释放二级指针
free(x);
strcpy常用于复制字符串而memcpy用于复制任意内容如字符数组结构体整形和类等。
strcpy不指定长度遇到"\0"自动结束,而memcpy由第三个长度决定复制的长度。
void *memcpy(void *memTo,const void *memFrom,size_t size)主要步骤
while(size-->0) *tempTo++=*tempFor++; return memTo;
char *strcpy(char* dest,const char*src)主要步骤
while((*strDest++=*strSrc++)!='\0');
28.二叉树的深度
int TreeDepth(BinaryTreeNode *pRoot)
{
if(pRoot==NULL)
return;
int nLeft=TreeDepth(pRoot->m_pLeft);
int nRight=TreeDepth(pRoot->m_pRight);
return (nLeft>nRight)?(nLeft+1):(nRight+1);
}
判断一个树是否为平衡二叉树
bool IsBalanced(BinaryTreeNode* pRoot);
{
if(pRoot==NULL)
return;
int nLeft=TreeDepth(pRoot->m_pLeft);
int nRight=TreeDepth(pRoot->m_pRight);
int diff=nLeft-nRight;
if(diff>1||diff<-1)
return false;
return IsBalanced(pRoot->m_pLeft&&pRoot->m_pRight);
}
29.哈希表构造用于存储每个字符出现的次数
char wordCount(char *pString)
{
if(pString==NULL)
return '\0';
int hashTable[256];
for(int i=0;i<256;i++)
{
hashTable[i]=0;
}
char *pHashKey=pString;
while(*pHashKey!='\0')
{
hashTable[*(pHashKey++)]++;
}
pHashKey=pString;//注意这里将指针归位,进行下一步操作
while(*pHashKey!='\0')
{
if(hashTable[*pHashKey]==1)
{
return *pHashKey;
}
pHashKey++;
}
return '\0';
}
30.二分查找法的使用去寻找一个排序的数组中k出现了几次。
int FindFirstK(int *data,int length,int k)
{
if(data==NULL||length<1)
return;
int middle=length>>1;
int temp=data[middle];
int start=end=middle;
if(temp==k)
{
if(data[middle-1]!=k)
return middle;
else
end=middle-1;
}
else if(temp>k)
{
end=middle-1;
}
else
start=middle+1;
return FindFirstK(data,kength,k);
}
31.翻转单词顺序
大致思路,分两个指针分别指向字符串头和尾。
void Reverse(char *pBegin,char *pEnd)
{
if(pBegin==NULL||pEnd==NULL)
return;
while(pBegin<pEnd)
{
char temp=*pBegin;
*pBegin=*pEnd;
*pEnd=temp;
pBegin++;pEnd--;
}
}
char *ReverseSentence(char *pData)
{
if(pData==NULL)
return;
while(*pBegin!='\0')
pEnd++;
pEnd--;
Reverse(pBegin,pEnd);
pBegin=pEnd=pData;
while(*pBegin!='\0')
{
if(*pBegin==' ')
{
pBegin++;
pEnd++;
}
else if(*pEnd==' '||*pEnd!='\0')
{
Reverse(pBegin,--pEnd);//注意这里的逻辑关系,如何计算得到下一个字符
pBegin=++pEnd;
}
else
{
pEnd++;
}
}
return pData;
}
32.连续子数组的最大和
int GetSum(int *pData,int length)
{
if(numbers==NULL||length<1)
return;
int currentSum=0;
int GreatestSum=0x80000000;//0x7FFFFFFF是int型的最大值,0x80000000是int型的最小值
//动规的话这里是int *dp=new int[length];
for(int i=0;i<length;i++)
{
if(currentSum<0)//动规的话这里是if(dp[i-1]<=0) i从1开始,dp[0]为array[0]
currentSum=pData[i];//动规的话这里是dp[i]=array[i];
else
currentSum+=pData[i];//动规的话这里为dp[i]=array[i]+dp[i-1];
if(currentSum>GreatestSum)
GreatestSum=currentSum;//此操作用于记录所遇到的最大值,也就是添加了一个变量来存储,这操作很优秀,如果比它小就不存储,比它大就每次更新。
}
return GreatestSum;
}
33.数值的整数次方
double PowerWithUnsignedExponent(double base,unsigned int exponent)//复杂度为O(log(n))
{
if(exponent==0)
result=1;
if(exponent==1)
result=base;
double result=PowerWithUnsignedExponent(base,exponent>>1);//求解n/2然后平方一下
result*=result;
if(exponent&0x1==1)
result*=base;
return result;
}
自己实现一个pow函数://复杂度为O(n)
double Power(double base,int exponent)
{
double result=1.0;
for(int i=1;i<exponent;i++)
result*=base;
return result;
}
34.树的子结构
BinaryTreeNode* HasSubTree(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
bool result=false;
if(pRoot1!=NULL&&pRoot2!=NULL)
{
if(pRoot1->m_nValue==pRoot2->m_nValue)
result=DoesTree1HasTree2(pRoot1,pRoot2)
if(!result)
result=HasSubTree(pRoot1->m_pLeft,pRoot2);
if(!result)
result=HasSubTree(pRoot1->m_pRight,pRoot2);
}
}
BinaryTreeNode* DoesTree1HasTree2(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
if(pRoot1==NULL)
return false;
if(pRoot2==NULL)
return true;
if(pRoot1->m_nValue!=pRoot2->m_nValue)
return false;
return DoesTree1HasTree2(pRoot1->m_pLeft,pRoot2->m_pLeft)&&DoesTree1HasTree2(pRoot1->m_pRight,pRoot2->m_pRight)
}
35.二叉搜索树的后序遍历序列
bool VerifyPostOrder(int sequence[],int length)
{
if(sequence==NULL||length<1)
return;
int root=sequence[length-1];
int i=0;
for(;i<length-1;i++)
{
if(sequence[i]>root)
break;
}
int j=i;
for(;j<length-1;j++)
{
if(sequence[j]<root)
return false;
}
bool left=true;
if(i>0)
left=VerifyPostOrder(sequence,i);
bool right=true;
if(i<length-1)
right=VerifyPostOrder(sequence+i,length-i-1);
return (left&&right);
}
36.二叉树的路径//需要添加一个vector数组存路径以及一个变量存当前和
void FindPath(BinaryTreeNode* pRoot,int expectedSum)
{
if(pRoot==NULL||expectedSum<0)
return;
vector<int> path;
int currentSum=0;
FindPath(pRoot,expectedSum,path,currentSum);
}
void FindPath(BinaryTreeNode* pRoot,int expectedSum,vector<int>& path,int ¤tSum)
{
currentSum+=pRoot->m_nValue;//这里的path和currentSum参数注意:引用是绑定到
path.push(pRoot->m_nValue);
bool isLeaf==(pRoot->m_pLeft==NULL&&pRoot->m_pRight);
if(currentSum==expectedSum&&isLeaf)
{
vector<int>::iterator iter=path.begin();
for(;iter!=path.end();iter++)
printf("%d",*iter);
}
if(pRoot->m_pLeft!=NULL)
FindPath(pRoot->m_pLeft,expectedSum,path,currentSum);
if(pRoot->m_pRight!=NULL)
FindPath(pRoot->m_pRight,expectedSum,path,currentSum);
currentSum-=pRoot->m_nValue;
path.pop_back();
}
37.链表删除增加练习
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void AddToTail(ListNode** pHead,int value)
{
ListNode* pNew=new ListNode();
pNew->m_nValue=value;
pNew->m_pNext=NULL;
if(*pHead==NULL)
{
*pHead=pNew;
}
else
{
ListNode* pNode=*pHead;
while(pNode->m_pNext!=NULL)
pNode=pNode->m_pNext;
*pNode->m_pNext=pNew;
}
}
void DeleteNode(ListNode** pHead,int Value)
{
ListNode* pDeleteNode=NULL;
if(*pHead->m_nValue==value)
{
pDeleteNode=*pHead;
*pHead=*pHead->m_pNext;
}
else
{
ListNode* pNode=*pHead;
while(pNode->m_pNext!=NULL&&pNode->m_pNext->m_nValue!=value)
{
pNode=pNode->m_pNext;
}
if(pNode->m_pNext!=NULL&&pNode->m_pNext->m_nValue==value)
{
pDeleteHead=pNode->m_pNext;
pNode->m_pNext=pNode->m_pNext->m_pNext;
}
}
if(pDeleteNode!=NULL)
{
delete pDeleteNode;
pDeleteNode=NULL;
}
}
38.要形成一个环形链表,即在链表走到尾结点时将指针指向头结点。
int Joseph(int n,int m)
{
if(numbers==NULL||length<1)
return;
list<int> lists;
for(int i=0;i<n;i++)
{
lists.push_back(i);
}
list<int>::iterator iter=lists.begin();
while(lists.size()>1)
{
for(int i=1;i<m;i++)
{
iter++;
if(iter==lists.end())
iter=lists.begin();
}
list<int>::iterator next=++current;
if(next==lists.end())
next=lists.begin();
}
}
BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)
{
BinaryTreeNpde* pLastNodeInList=NULL;
ConvertNode(pRootOfTree,&pLastNodeInList;)
}
void ConvertNode(BinaryTreeNode* pNode,BinaryTreeNode** pLasrNodeInList)
{
}
BinaryTreeNode* pLastOfNode;
&
**