挑战数据结构与算法面试题——80题全解析(一)

题目来源“数据结构与算法面试题80道”。这是第一部分,包含其中的第1题到第5题。
在此给出我的解法,如你有更好的解法,欢迎留言。

这里写图片描述

问题分析:二叉查找树是一种二叉树的结构,其中,根节点的值大于左子树的值,小于右子树的值。而二叉查找树的中序遍历即为排序的结果。对于根节点,前驱指针指向左子树中最大的节点,同理,后驱指针指向右子树中最小的节点,如下图所示:

这里写图片描述

树是一种递归的结果,因此,对于左右子树,也需要执行相同的操作。

方法:

BSTreeNode* convert(BSTreeNode *root){
    if (NULL == root ||
        (NULL == root->m_pLeft && NULL == root->m_pRight)){
        return root;
    }

    convert(root->m_pLeft);
    BSTreeNode* p_left = root->m_pLeft;
    while(p_left->m_pRight != NULL){
        p_left = p_left->m_pRight;
    }
    root->m_pLeft = p_left;
    p_left->m_pRight = root;

    convert(root->m_pRight);
    BSTreeNode* p_right = root->m_pRight;
    while(p_right->m_pLeft != NULL){
        p_right = p_right->m_pLeft;
    }
    root->m_pRight = p_right;
    p_right->m_pLeft = root;

    BSTreeNode *p = root;
    while (NULL != p->m_pLeft){
        p = p->m_pLeft;
    }
    return p;
}

这里写图片描述

问题分析:栈的特点是先进后出。要能够取出当前的最小值,需要用另一个栈保存当前的最小值,所以可采用“双栈”的结构,一个栈保存所有的值,另一个栈保存当前的最小值。

方法:

template <class Type> class min_stack{
    private:
        stack<Type> s1;
        stack<Type> s2;
    public:
        min_stack(){}
        ~min_stack(){}

        Type min(){
            if (!s2.empty()){
                return s2.top();
            }
        }

        void push(Type a){
            s1.push(a);
            if (s2.empty() || a <= s2.top()){
                s2.push(a);
            }
        }

        Type pop(){
            if (!s1.empty() && !s2.empty()){// 非空
                if (s1.top() == s2.top()){
                    s2.pop();
                }
                return s1.pop();
            }
        }
};

这里写图片描述

问题分析:在数组的每一个位置处保存当前的最大值,当前的最大值组成为:

f(xi)={xif(xi1)+xi if i=0orf(xi1)0 if f(xi1)>0 f ( x i ) = { x i  if  i = 0 o r f ( x i − 1 ) ⩽ 0 f ( x i − 1 ) + x i  if  f ( x i − 1 ) > 0

解决方案:

int get_max_subarray(int *a, int length, bool &is_array_ok){
    if (NULL == a || length <= 0){
        is_array_ok = false;
        return 0;
    }

    int *p_h_a = (int *)malloc(sizeof(int) * length);
    // 遍历数组
    int max_num = 0;
    for (int i = 0; i < length; i++){
        if (i == 0 || (i > 0 && p_h_a[i-1] <= 0)){
            p_h_a[i] = a[i];
        }else{
            p_h_a[i] = p_h_a[i-1] + a[i];
        }
        if (max_num < p_h_a[i]) max_num = p_h_a[i];
    }
    free(p_h_a);

    is_array_ok = true;
    return max_num;
}

这里写图片描述

问题分析:核心是树的遍历,注意题目中“路径”的定义,是从根节点到叶子节点。先序遍历正好是从根节点开始,因此可以利用先序遍历的过程来实现这个过程。

方法:

void print_vector(vector<BinaryTreeNode *> &v){
    vector<BinaryTreeNode *>::iterator it;
    for (it = v.begin(); it != v.end(); it ++){
        printf("%d\t", (*it)->m_nValue);
    }
    printf("\n");
}

void pre_order_route(BinaryTreeNode *p, int num, vector<BinaryTreeNode *> &q, int &current){
    if (NULL == p) return;

    current += p->m_nValue;
    q.push_back(p);

    bool is_leaf = (NULL == p->m_pLeft) && (NULL == p->m_pRight);

    if (current == num && is_leaf){
        print_vector(q);
    }

    if (NULL != p->m_pLeft){
        pre_order_route(p->m_pLeft, num, q, current);
    }

    if (NULL != p->m_pRight){
        pre_order_route(p->m_pRight, num, q, current);
    }

    current -= (*(q.end() - 1))->m_nValue;
    q.pop_back();
}


void print_route(BinaryTreeNode *root, int num){
    vector<BinaryTreeNode *> q;// 用队列保存已经访问过的节点
    int current = 0;
    pre_order_route(root, num, q, current);
}

这里写图片描述

问题分析:这是一道比较经典的题目,查找最小的k个元素,最简单的方法就是对这n个整数排序,排序完成后,直接输出前k个最小的元素。那么最快的排序方法是快速排序,其算法的时间复杂度为O(nlogn)。是否还存在比这个更快的方法呢?

方法一:利用快速排序的思想,时间复杂度为O(n)

按照某个点将数组划分成左右两部分,左边的数都小于该划分节点,右边的数都大于该划分节点。如果最终该划分节点的位置小于k-1,则在右边节点中继续划分;如果最终该划分节点的位置大于k-1,则在左边节点中继续划分。这个过程直到最终的划分节点的位置正好为k-1。

int swap(int *a, int start, int end, int point_index){
    int par_point = a[point_index];
    while (start < end){
        if (a[start] >= par_point && a[end] <= par_point){
            int tmp = a[start];
            a[start] = a[end];
            a[end] = tmp;
            start ++;
            end --;
        }else if(a[start] < par_point){
            start ++;
        }else{
            end --;
        }
    }
    return start;
}

void get_min_k(int *a, int length, int k){
    if (k > length || NULL == a || length <= 0) return;

    int new_index = swap(a, 0, length-1, k);
    while (true){
        if (new_index == k) break;
        else if (new_index > k){
            new_index = swap(a, 0, new_index, k);
        }else{
            new_index = swap(a, new_index, length-1, k);
        }
    }
}

方法二:利用堆排序,时间复杂度为O(nlogk)

上述方法的缺点是其对数组进行了修改,在堆排序中,可采用小顶堆,其中堆的大小为k,若此时堆的大小小于k时,则将数插入堆中;若此时堆中的大小大于等于k,则比较堆中最大的整数与待插入整数的大小,插入较小的整数。

void get_min_k(int *a, int length, int k, set<int> &s){
    if (k > length || NULL == a || length <= 0) return;

    for (int i = 0; i < length; i++){
        if (s.size() < k){
            s.insert(a[i]);
        }else{
            set<int>::iterator it = --s.end();
            if (a[i] < *it){
                s.erase(*it);
                s.insert(a[i]);
            }
        }
    }
}
已标记关键词 清除标记
1.算法是程序的灵魂,优秀的程序在对海量数据处理时,依然保持高速计算,就需要高效的数据结构算法支撑。2.网上数据结构算法的课程不少,但存在两个问:1)授课方式单一,大多是照着代码念一遍,数据结构算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了2)说是讲数据结构算法,但大多是挂羊头卖狗肉,算法讲的很少。 本课程针对上述问,有针对性的进行了升级 3)授课方式采用图解+算法游戏的方式,让课程生动有趣好理解 4)系统面的讲解了数据结构算法, 除常用数据结构算法外,还包括程序员常用10大算法:二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、马踏棋盘算法。可以解决面试遇到的最短路径、最小生成树、最小连通图、动态规划等问及衍生出的面试题,让你秒杀其他面试小伙伴3.如果你不想永远都是代码工人,就需要花时间来研究下数据结构算法。教程内容:本教程是使用Java来讲解数据结构算法,考虑到数据结构算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问、八皇后问算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。学习目标:通过学习,学员能掌握主流数据结构算法的实现机制,开阔编程思路,提高优化程序的能力。
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页