面试题(一)

注:不喜勿喷。


//1.输入一个链表的头结点,从尾到头反过来打印出每个结点的值;(单向链表)
struct ListNode
{
    int key;
    ListNode* next;
};
//利用栈的先进后出的性质
void printReverList(ListNode* root)
{
    assert(root);
    //创建一个栈
    stack<ListNode*> s;
    ListNode* cur = root;
    while(cur)
    {
        s.push(cur);
        cur = cur->next;
    }
    while(!s.empty())
    {
        printf("%d ",s.top()->key);
        s.pop();
    }
}
//递归
void printRevList(ListNode* root)
{
    if(root == NULL)
    {
        return;
    }
    printRevList(root->next);
    cout<<root->key;
}

//2.输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的
//结果中国都不含重复的数字,例如:输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列
//{4,7,2,1,5,3,8,6},则重建二叉树并输出它的头结点,二叉树定义如下:
struct BinaryTreeNode
{
    int value;
    BinaryTreeNode* left;
    BinaryTreeNode* right;
};
//前序:根左右(第一个是根节点){1,2,4,7,3,5,6,8}
//中序:左根右{4,7,2,1,5,3,8,6}
typedef BinaryTreeNode Node;
Node* ReBuildBinaryTree(int*& prev, int* inBegin, int* inEnd)
{
    assert(prev && inBegin && inEnd);
    if(inBegin > inEnd || *prev == '\0')
    {
        return NULL;
    }
    Node* root = new Node(*prev);
    int* div = inBegin;
    while(div <= inEnd)
    {
        if(*div == *prev)
        {
            if(div-1 >= inBegin)
            {
                root->left = ReBuildBinaryTree(++prev, inBegin, div-1);
            }
            else
                root->left = NULL;
            if(div+1 <= inEnd)
            {
                root->right = ReBuildBinaryTree(++prev, div+1 , inEnd);
            }
            else
            {
                root->right = NULL;
            }
            break;
        }
        ++div;
    }
    return root;
}

//3.给定一棵二叉树和其中一个结点,如何找到中序遍历序列的下一个节点?树中的结点除了有两个分别
//指向左右子节点的指针,还有一个指向父节点的指针;
//分析:当前给定结点是否有右子树?中序:左根右
//有:访问当前节点的右子树最左结点
//没有:是否是父节点的左孩子?
   //是:直接访问父节点
   //不是:向上遍历,直到找到结点是父亲的左孩子,然后访问父亲结点
struct Node
{
    int _key;
    Node* _left;
    Node* _right;
    Node* _parent;
};
Node* FindNextNode(Node* root)
{
    assert(root);
    Node* cur = root;
    if(cur->_right)
    {
        cur = cur->_right;
        while(cur->_left)
        {
            cur = cur->_left;
        }
    }
    else
    {
        Node* parent = cur->_parent;
        if(parent == NULL)//此时是根节点,且根节点没有右子树
            return NULL;
        while(parent != NULL && cur != parent->_left)
        {
            cur = parent;
            parent = cur->_parent;
        }
        cur = parent;
        //if(parent->_left == cur)
        //{
        //  cur = parent;
        //}
        //else
        //{
        //  while(parent != NULL && cur != parent->_left)
        //  {
        //      cur = parent;
        //      parent = cur->_parent;
        //  }
        //  if(parent == NULL)//此时的结点是中序遍历的最后一个结点
        //      return NULL;
        //  cur = parent;
        //}
    }
    return cur;
}    

//4.利用两个栈实现一个队列,队列的声明如下,请实现它的两个函数appendTail和deleteHead,
//分别完成在队列尾部插入结点和在队列头部删除节点的功能;
//分析:队列---->先进先出     栈---->后进先出(先进后出)
template <typename T>
class CQueue
{
private:
    stack<T> stack1;//顺
    stack<T> stack2;//逆
public:
    CQueue();
    ~CQueue();
    void appendTail(const T& node);
    T deleteHead();
};
//分析:两个栈,一个放顺着的队列,一个放逆着的队列,
//如果要尾插,直接在顺着的队列里面直接push
//如果要头删,直接在逆着的队列里面pop
template <typename T>
void CQueue::appendTail(const T& node)//尾插
{
    stack1.push(node);
}
T CQueue::deleteHead()//头删
{
    assert(!stack1.empty() || !stack2.empty());
    if(stack2.empty())
    {
        while(!stack1.empty())
        {
            T& node = stack1.top();
            stack1.pop();
            stack2.push(node);
        }
    }
    T& node = stack2.top();
    stack2.pop();
    return node;
}
//思考,如果我要打印这个队列的话,如下方法:
//先打印逆着的栈,逆着打印
//再打印顺着的栈,顺着打印

//5.求斐波那契数列的第N项,写入一个函数,输入n,求斐波那契数列的第n项,斐波那契数列定义如下:
//f(n) = 0                n = 0
//f(n) = 1                n = 1
//f(n) = f(n-1)+f(n-2)    n > 1
//递归法:不好,会出现栈溢出现象,效率也差
int Fib(int n)
{
    assert(n >= 0);
    if(n == 0)
    {
        return 0;
    }
    else if(n == 1)
    {
        return 1;
    }
    else
    {
        return Fib(n-1) + Fib(n-2);
    }
}
//循环:
int FibNoR(int m)
{
    assert(m >= 0);
    if(m == 0)
        return 0;
    if(m == 1)
        return 1;
    int f1 = 0, f2 = 0, f3 = 0;
    int n = 0;
    while(1)
    {
        if(n == 0)
            f1 = 0;
        else if(n == 1)
            f2 = 1;
        else
        {
            f3 = f1 + f2;
            f1 = f2;
            f2 = f3;
        }
        if(n == m)
            return f3;
        n++;
    }
}
long long Fibonacci(unsigned n)
{
    int result[2] = {0,1};
    if(n < 2)
    {
        return result[n];
    }
    long long fibone = 1;
    long long fibtwo = 2;
    long long fib = 0;
    for(unsigned int i = 2; i <= n; i++)
    {
        fib = fibone + fibtwo;
        fibone = fibtwo;
        fibtwo = fib;
    }
    return fib;
}
//题目变换:青蛙跳台阶问题:
//一只青蛙以此可以跳上一级台阶,也可以跳上2级台阶,求该青蛙跳上n级台阶总共有多少种跳法;
//斐波那契数列求解问题;

//6.对公司员工的年龄进行排序,要求时间效率为O(n);(可以使用少部分辅助内存)
//分析:员工年龄设置在100岁以内,定义一个100个元素的数组,其中某个年龄增加就在相应下标中的
//元素内容+1,以此来将所有年龄段相同的人数进行以此统计,然后以便后续排序;
void SortAges(int ages[], int length)
{
    assert(ages && length > 0);
    const int bigAge = 99;
    int ageArr[bigAge+1];//0--99岁的人
    //初始化数组
    for(int i = 0; i < bigAge+1; i++)
    {
        ageArr[i] = 0;
    }
    for(int i = 0; i < length; i++)
    {
        if(ages[i] >= 0 && ages[i] < bigAge+1)
        {
            ++ageArr[ages[i]];
        }
    }
    //年龄:0-99
    //数组ageArr[]:统计出每个年龄段出现的人数
    //接下来进行排序
    int index = 0;
    for(int i = 0; i < bigAge+1; i++)
    {
        for(int j = 0; j < ageArr[i]; j++)
        {
            ages[index] = i;
            ++index;
        }
    }
}

//7.把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组
//的一个旋转,输出旋转数组的最小元素。例如:数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的
//最小元素值是1
//分析:前后两个指针一起走,第一个指针指向第一个元素,第二个指针指向最后一个元素,求出中间
//元素,如果比第一个指针大,那么移动第一个指针,如果比第二个指针小,那么移动第二个指针,这样
//我们求的最小元素所在的区间会越来越小,最终两个指针会相邻;(二分查找法)
//几种特殊情况:a.该数组旋转0;b.该数组的元素是{1,1,0,0,0,1,1,1,1}这样的旋转
int MinOrder(int* arr, int begin, int end);
int Min(int* arr, int length)
{
    assert(arr && length > 0);
    if(arr[0] < arr[1])
    {
        return arr[0];
    }
    int begin = 0;
    int end = length - 1;
    int mid = begin + ((end - begin) >> 1);
    while(begin < end)
    {
        if(end - 1 == begin)
        {
            break;
        }
        else if(arr[begin] > arr[mid])
        {
            end = mid;
            mid = begin + ((end - begin) >> 1);
        }
        else if(arr[begin] == arr[mid] == arr[end])
        {
            return MinOrder(arr,begin,end);
        }
        else
        {
            begin = mid;
            mid = begin + ((end - begin) >> 1);
        }
    }
    cout<<"end: "<<end<<endl;
    return arr[end];
}
//当arr[mid] == arr[begin] == arr[end]时,就需要进行顺序查找
int MinOrder(int* arr, int begin, int end)  
{
    int key = arr[begin];
    int i = begin + 1;
    for(; i <= end; i++)
    {
        if(key > arr[i])
        {
            break;
        }
        key = arr[i];
    }
    cout<<"i: "<<i<<endl; 
    return arr[i];
}

//8.请设计一个函数,用来判断在一个矩阵中是否存在一条包含某个字符串种所有的字符的路径。
//路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左,右,上,下移动一格。如果一条
//路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如:在下面的3*4的矩阵中包含一条
//字符串“bfce”的路径,但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了
//矩阵中的第一行第二个各自之后,路径不能再次进入这个格子;
//分析:类似于迷宫求解问题,走过的路径不能再走,两种方法:递归和循环
//循环
typedef struct Entry
{
    int _x;
    int _y;
}Entry;
bool IsGo(Entry& cur, char arr[][3], int row, int col, char str);
bool FindPath(char arr[][3],int row, int col, char* str)
{
    assert(arr);
    if(str == NULL)
        return false;
    Entry entry;
    for(int i = 0; i < row; i++)
    {
        int j = 0;
        for(; j < col; j++)
        {
            if(arr[i][j] == *str)
            {
                entry._x = i;
                entry._y = j;
                /*arr[i][j] = 1;*/
                break;
            }
        }
        if(j != col)
        {
            break;
        }
    }
    stack<Entry> s;
    s.push(entry);
    while(*str != '\0' && !s.empty())
    {
        Entry cur = s.top();
        if(arr[cur._x][cur._y] != '1')
        {
            ++str;
            if(*str == '\0')
            {
                arr[cur._x][cur._y] = '1';
                for(int i = 0; i < row; i++)
                {
                    for(int j = 0; j < col; j++)
                    {
                        cout<<arr[i][j]<<" ";
                    }
                    cout<<endl;
                }
                return true;
            }
        }
        arr[cur._x][cur._y] = '1';
        Entry next = cur;
        //上
        next._x = next._x - 1;
        if(IsGo(next,arr,row,col,*str))
        {
            s.push(next);
            continue;
        }
        //右
        next = cur;
        next._y = next._y + 1;
        if(IsGo(next,arr,row,col,*str))
        {
            s.push(next);
            continue;
        }
        //下
        next = cur;
        next._x = next._x + 1;
        if(IsGo(next,arr,row,col,*str))
        {
            s.push(next);
            continue;
        }
        //左
        next = cur;
        next._y = next._y - 1;
        if(IsGo(next,arr,row,col,*str))
        {
            s.push(next);
            continue;
        }
        if(arr[cur._x][cur._y] == 1)
        {
            arr[cur._x][cur._y] = *(str);
        }
        --str;
        s.pop();
    }
    //for(int i = 0; i < row; i++)
    //{
    //  for(int j = 0; j < col; j++)
    //  {
    //      cout<<arr[i][j]<<" ";
    //  }
    //  cout<<endl;
    //}
    return false;
}
bool IsGo(Entry& cur, char arr[][3], int row, int col, char str)
{
    if(cur._x >= 0 && cur._x < row && cur._y >= 0 && cur._y < col
        && arr[cur._x][cur._y] == str)
    {

        /*arr[cur._x][cur._y] = 1;*/
        return true;

        /*if(arr[cur._x][cur._y] == 1)
        {
        arr[cur._x][cur._y] = str;
        }*/
    }
    return false;
}
//递归
bool hasPathCore(char* matrix,int rows,int cols,int row,int col,char* str,int index, bool* visited);
bool hasPath(char* matrix, int rows,int cols,char* str)
{
    if(NULL == matrix || rows < 1 || cols < 1 || NULL == str)
    {
        return false;
    }
    //创建了一个和目标数组一样大小的数组空间,然后将路径存放在该数组空间中;
    bool* visited = new bool[rows*cols];
    memset(visited,0,rows*cols);//bool是一个字节
    int index = 0;
    for(int i = 0; i < rows; i++)
    {
        for(int j = 0; j < cols; j++)
        {
            if(hasPathCore(matrix,rows,cols,i,j,str,index,visited))
            {
                for(int i = 0; i < rows; i++)
                {
                    for(int j = 0; j < cols; j++)
                    {
                        cout<<visited[i*cols+j]<<" ";
                    }
                    cout<<endl;
                }
                delete[] visited;
                return true;
            }
        }
    }
    delete[] visited;
    return false;
}
bool hasPathCore(char* matrix,int rows,int cols,int row,int col,char* str,int index, bool* visited)
{
    if(str[index] == '\0')
    {
        return true;
    }
    bool path = false;
    if(row >= 0 && col >= 0 && row < rows && col < cols
        && matrix[row*cols + col] == str[index])
    {
        ++index;
        visited[row*cols+col] = true;
        path = hasPathCore(matrix,rows,cols,row-1,col,str,index,visited) 
            || hasPathCore(matrix,rows,cols,row,col+1,str,index,visited)
            || hasPathCore(matrix,rows,cols,row+1,col,str,index,visited)
            || hasPathCore(matrix,rows,cols,row,col-1,str,index,visited);
        //(只要有一个为真就可以不再继续向下走)
        if(!path)
        {
            --index;
            visited[row*cols+col] = false;
        }
    }
    return path;
}

//9.地上有一个m行n列的方格,一个机器人从坐标(0,0)的各自开始移动,它每次可以向左,右,上下
//移动一格,但是不能进入行坐标和列坐标的位数之和大于k的格子,例如:当k为18时,机器人能够
//进入方格(35,37),因为3+5+3+7=18;但是它不能进入方格(35,38),因为3+5+3+8=19;请问机器人
//能够达到多少个格子?



//10.给你一根长度为n的绳子,请把绳子剪成m段(int m,n;n>1,m>1)每段绳子的长度记为k[0],k[1]...
//k[m],请问k[0]*k[1]*...k[m]可能的最大成绩是多少?
//动态规划算法和贪婪算法------->求一个问题的最优解
//动态规划:把每一个大问题分解成小问题,直到小问题可解,然后从小问题解决再到大问题
//大到小分析,小到大解决
//当len=1,最大成绩是0;当len=2,最大成绩是1;当len=3,最大成绩是2;
//空间复杂度O(N),时间复杂度O(N*N)
int Max(int len)
{
    assert(len > 0);
    if(len < 2)
    {
        return 0;
    }
    if(len == 2)
    {
        return 1;
    }
    if(len == 3)
    {
        return 2;
    }
    int* product = new int[len+1];
    product[0] = 0;
    product[1] = 1;
    product[2] = 2;
    product[3] = 3;
    int max = 0;
    for(int i = 0; i < 4; i++)
    {
        cout<<product[i]<<" ";
    }
    for(int i = 4; i <= len; i++)
    {
        for(int j = 1; j <= i/2; j++)
        {
            int pro = product[j]*product[i-j];
            if(max < pro)
                max = pro;
        }
        product[i] = max;
        cout<<max<<" ";
    }
    cout<<endl;
    delete[] product;
    return max;
}
//贪心算法:
//时间复杂度,空间复杂度都是O(1)
//每次都找子问题的最优解(最优的子问题),将所有子问题的最优解乘在一起得到最优解
//如下最好的选择是当n>=5时,减到3最好,2*3=6;当n=4时,减到2好,2*2=4;
int MaxGreed(int len)
{
    if(len < 2)
    {
        return 0;
    }
    if(len == 2)
    {
        return 1;
    }
    if(len == 3)
    {
        return 2;
    }
    int threeCount = len/3;
    if(len % 3 == 1)
    {
        threeCount -= 1;
    }
    int twoCount = (len - threeCount*3)/2;
    //计算出2的个数,因为每次都是最优解,所以2可能最后剩1个,也可能最后剩2个
    return (int)(pow(2.0,twoCount)) * (int)(pow(3.0,threeCount));
}

//11.实现函数double power(double base,int exponent),求base的exponent次方,不得
//使用库函数,同时不需要考虑大数问题;
double GetPowNum(double base,int exponent);
#define EXP 0.0000001
double power(double base,int exponent)
{
    //浮点数判0
    if(base > -EXP && base < EXP && exponent < 0)//非法
    {
        return 0.0;
    }
    int newexponent;
    if(exponent < 0)
    {
        newexponent = exponent * (-1);
    }
    else if(exponent == 0)
        return 1.0;
    else if(exponent == 1)
        return base;
    double result = GetPowNum(base,newexponent);
    if(exponent < 0)
    {
        result = 1.0/result;
    }
    return result;  
}
double GetPowNum(double base,int exponent)
{
    double result = 1.0;
    for(int i = 1; i <= exponent; i++)
    {
        result *= base;
    }
    return result;
}
//改进GetPowNum()函数,使其更高效----递归
double GetPwoNumR(double base,int exponent)
{
    if(exponent == 0)
    {
        return 1.0;
    }
    if(exponent == 1)
    {
        return base;
    }
    double result = GetPwoNumR(base,exponent>>1);//先快速求出一半次方的数
    result *= result;
    if(exponent % 2 != 0)//如果次方是奇数
    {
        result *= base;
    }
    return result;
}

//12.打印从1到最大的n位数:输入数字n,按顺序打印出从1到最大的n位十进制数,比如
//输入3,则打印出1,2,3一直到最大的3位数999
//陷阱:考虑大数问题
void printOneToN(int n)
{
    if(n <= 0)
    {
        cout<<0<<endl;
    }
    else
    {
        int max = 9;
        while(n > 1)
        {
            --n;
            max = max*10+9;
        }
        for(int i = 1; i <= max; i++)
        {
            cout<<i<<" ";
        }
        cout<<endl;
    }
}
//改进版----考虑大数运算,利用字符串来解决问题
bool AddOneAndIsFlow(char* Number);
void printNumber(char* Number);
void printOneToNNew(int n)
{
    if(n <= 0)
        return;
    char* digit = new char[n+1];
    memset(digit,'0',n);
    digit[n] = '\0';
    while(!AddOneAndIsFlow(digit))
    {
        printNumber(digit);
    }
    delete[] digit;
}
bool AddOneAndIsFlow(char* Number)
{
    int len = strlen(Number);
    for(int i = len-1; i >= 0; --i)
    {
        int num = Number[i] - '0' + 1;
        if(num == 10)
        {
            if(i == 0)
                return true;//溢出
            Number[i] = '0';
        }
        else
        {
            Number[i] = num + '0';
            break;
        }
    }
    return false;
}
void printNumber(char* Number)
{
    int len = strlen(Number);
    bool isBegin0 = true;
    for(int i = 0; i < len; i++)
    {
        if(isBegin0 && Number[i] != '0')
        {
            isBegin0 = false;
        }
        if(!isBegin0)
        {
            cout<<Number[i];
        }
    }
    printf("\t");
}
//倒着输出
bool subOneAndIsNon(char* Number);
void printNToOne(int n)
{
    if(n <= 0)
        return;
    char* number = new char[n+1];
    memset(number,'9',n);
    number[n] = '\0';
    printNumber(number);
    while(!subOneAndIsNon(number))
    {
        printNumber(number);
    }
    delete[] number;
}
bool subOneAndIsNon(char* Number)
{
    int len = strlen(Number);
    for(int i = len-1; i >= 0; --i)
    {
        int num = Number[i] - '0' - 1;
        if(num == -1)
        {
            if(i == 0)
                return true;
            Number[i] = '9';
        }
        else
        {
            Number[i] = num + '0';
            break;
        }
    }
    return false;
}

//13.删除链表的节点:
//在O(1)时间内删除链表节点,给定单向链表的头指针和一个节点指针,定义一个函数在
//O(1)时间内删除该节点,链表节点与函数的定义如下:
struct ListNode
{
    int value;
    ListNode* next;
};
void DeleteNode(ListNode** pListHead,ListNode* Delete)
{
    assert(*pListHead || Delete);
    ListNode* root = *pListHead;
    if(root == Delete)
    {
        *pListHead = Delete->next;
        delete Delete;
        Delete = NULL;
    }
    else
    {
        ListNode*& next = Delete->next;
        if(NULL == next)
        {
            delete Delete;
            Delete = NULL;
        }
        else
        {
            swap(next->value,Delete->value);
            Delete->next = next->next;
            delete next;
            next = NULL;
        }
    }
}

//14.删除链表中的重复的节点:在一个排序的链表中,如何删除重复节点?
void DeleteRepNode(ListNode* root)
{
    assert(root);
    ListNode* child = root->next;
    ListNode* parent = root;
    int num = root->value;
    while(child)
    {
        if(num == child->value)
        {
            //删除节点
            ListNode* next = child->next;
            if(NULL == next)
            {
                delete child;
                parent->next = NULL;
                return;
            }
            child->value = next->value;
            child->next = next->next;
            delete next;
        }
        else
        {
            num = child->value;
            parent = child;
            child = child->next;
        }
    }
}

//15.正则表达式:请实现一个函数用来匹配包含‘.’和‘*’的正则表达式,模式中的
//字符‘.’表示任意一个字符,而‘*’表示它前面的字符可以出现任意次(0次),例如:
//字符串“aaa”,与模式“a.a”和“ab*ac*a”匹配,但与“aa.a”和“ab*a”均不匹配
bool MatchCore(char* str,char* match);
bool IsMatch(char* str,char* match)
{
    assert(str || match);
    return MatchCore(str,match);
}
bool MatchCore(char* str,char* match)
{
    if(*str == '\0' && *match == '\0')
        return true;
    if(*str != '\0' && *match == '\0')
        return false;
    if(*(match + 1) == '*')
    {
        //第一个字符可能存在,可能不存在
        if(*str == *match /*|| (*match == '.' && *str != '\0')*/)
        {
            return MatchCore(str+1,match+2)
                || MatchCore(str+1,match)
                || MatchCore(str,match+2);
        }
        else if(*match == '.' && *str != '\0')
        {
            return MatchCore(str+1,match+2)
                || MatchCore(str,match+2);
        }
        else
        {
            return MatchCore(str,match+2);
        }
    }
    if(*str == *match || (*match == '.' && *str != '\0'))
    {
        return MatchCore(++str,++match);
    }
    return false;
}

//16.请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串“+100”
//"5e2","-123","3.1234","-1E-16"都表示数值,但是“12e”,"1a3.14","1.2.3","12e+5.4"
//都不是
//A[.[B]][E|eC]或者.B[E|eC]表示数字的格式,B是无符号整数,A,C是整数可为正负
bool JudgeInteger(const char** str);
bool JudgeUnsignedInteger(const char** str);
bool IsNumeric(const char* str)
{
    assert(str);
    bool numeric = JudgeInteger(&str);
    if(*str == '.')
    {
        ++str;
        //只是过滤掉数字,因为.后面不一定非要有数字,如123.也行,所以用||
        numeric = JudgeUnsignedInteger(&str) || numeric;
    }
    if(*str == 'e' || *str == 'E')
    {
        ++str;
        //过滤掉数字,但是e后面必须要有数字所以是&&
        numeric = JudgeInteger(&str) && numeric;
    }
    return numeric && *str == '\0';
}
bool JudgeInteger(const char** str)
{
    if(**str == '+' || **str == '-')
    {
        ++(*str);
    }
    return JudgeUnsignedInteger(str);
}
bool JudgeUnsignedInteger(const char** str)
{
    const char* before = *str;
    while(**str != '\0' && **str >= '0' && **str <= '9')
    {
        ++(*str);
    }
    return before < *str;//相等说明已经到‘\0’或者其他不是数字的字符了
}

//17.奇数放在偶数前面
//输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半
//部分,所有偶数位于数组的后半部分
//一定要注意写的代码要有常性,试用于任何场景
bool Event(int x)//满足的某种条件
{
    return (x & 1) == 1;//奇数
}
void Reorder(int* pData,size_t len,bool (*func)(int))
{
    int* begin = pData;
    int* end = pData + len - 1;
    while(begin < end)
    {
        if(begin < end && (*func)(*begin))
        {
            ++begin;
        }
        if(begin < end && !(*func)(*end))
        {
            --end;
        }
        if(begin < end)
        {
            swap(*begin,*end);
        }
    }
}
void ReorderArr(int* pData,size_t len)
{
    assert(pData && len > 0);
    Reorder(pData,len,Event);
    for(size_t i = 0; i < len; i++)
    {
        cout<<pData[i]<<" ";
    }
    cout<<endl;
}

//18.链表中倒数第K个节点:
//输入一个链表,输出该链表中倒数第k个节点,为了符合大多数人的习惯,本题从1开始计数,即链表
//链表的尾节点是倒数第1个节点。例如:一个链表中有6个节点,从头开始,他们的值一次是1,2,3,4,5,6
//这个链表的倒数第三个节点的值为4的节点,链表定义如下:
struct _ListNode
{
    int _value;
    _ListNode* _next;
};
typedef _ListNode Node;
Node* FindReKNode(Node* root, const int k)
{
    assert(root && k > 0);
    Node* prev = root;
    int count = 0;
    Node* cur = root;
    while(prev)
    {
        if(count == k)
        {
            cur = cur->_next;
        }
        else if(count < k)
        {
            ++count;
        }
        prev = prev ->_next;    
    }
    if(count != k)
    {
        return NULL;
    }
    return cur;
}

//19.链表中环的入口节点:
//如果有一个链表中包含环,如何找出环的入口节点?例如:在链表中,环的入口节点是3;
//分析:1.如何判断有环:两个指针,一个走一步,一个走两步,相遇就有环
//      2.如何找到环的入口:两个指针,一个先走环中节点个数步,然后两个一起走,相遇就是入口
//      3.如何判断环中有几个节点:第一次相遇的节点在环中,以此为标记再走一圈并计数,相遇就是节点数目
Node* MeetNode(Node* root)
{
    assert(root);
    Node* quickptr = root;
    Node* slowptr = root;
    while(quickptr && slowptr)
    {
        slowptr = slowptr->_next;
        if(slowptr == nullptr)
        {
            break;
        }
        quickptr = slowptr->_next;
        if(quickptr == slowptr)
        {
            return slowptr;
        }
    }
    return NULL;
}
//找环中的节点个数
int GetNodeCount(Node* root)
{
    if(nullptr == root)
    {
        return 0;
    }
    int count = 1;
    Node* cur = root->_next;
    while(cur != root)
    {
        ++count;
        cur = cur->_next;
    }
    return count;
}
//判断环的入口在哪里
Node* WhereIsEntry(Node* root)
{
    assert(root);
    Node* meet = MeetNode(root);//找到是否是环
    int k = GetNodeCount(meet);
    Node* prev = root;
    Node* entry = root;
    int count = 0;
    while(prev && k > 1)
    {
        if(count == k)
        {
            entry = entry->_next;
        }
        else if(count < k)
        {
            ++count;
        }
        prev = prev->_next;
        if(entry == prev)
        {
            return entry;
        }
    }
    return NULL;
}

//20.反转链表
//定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点,使链表节点定义
//如下:
struct ListNodeR
{
    int _key;
    ListNodeR* _next;
    ListNodeR(int key)
        :_key(key),_next(NULL)
    {}
};
class List
{
    typedef ListNodeR Node;
private:
    Node* root;
public:
    List()
        :root(NULL)
    {}
    void PushBack(int key)
    {
        if(root == NULL)
        {
            root = new Node(key);
        }
        else
        {
            Node* cur = root;
            while(cur->_next)
            {
                cur = cur->_next;
            }
            Node* node = new Node(key);
            cur->_next = node;
        }
    }
    Node* getRoot()
    {
        return root;
    }
    void setRoot(Node* cur)
    {
        root = cur;
    }
    Node* ListR(Node* root)
    {
        assert(root);
        //只有一个节点
        if(root->_next == NULL)
        {
            return root;
        }
        //至少有两个节点
        Node* cur = root;
        Node* next = root->_next;
        Node* nnext = next;
        while(nnext)
        {
            nnext = next->_next;
            next->_next = cur;
            if(cur == root)
            {
                cur->_next = NULL;
            }
            cur = next;
            next = nnext;
        }
        root = cur;
        return root;
    }
    void show(Node* root)
    {
        assert(root);
        while(root)
        {
            cout<<root->_key<<" ";
            root = root->_next;
        }
        cout<<endl;
    }
};
void test()
{
    List list;
    list.PushBack(1);
    //list.PushBack(2);
    /*list.PushBack(3);
    list.PushBack(4);
    list.PushBack(5);*/
    list.show(list.getRoot());
    list.setRoot(list.ListR(list.getRoot()));
    list.show(list.getRoot());
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值