剑指offer-笔记

里面挺多细节的东西,每隔一段时间还要来复习一下。


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 &currentSum)
{
    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;
&
**

猜你喜欢

转载自blog.csdn.net/Parallel2333/article/details/82805352
今日推荐