基础面试题 — 数据结构与算法「建议收藏」

基础面试题 — 数据结构与算法「建议收藏」数据结构是对实际问题中的数据元素及相互间的联系的抽象。一般用线性表来表示常用数据结构,线性表分为顺序存储的顺序表和连式存储的链表。 在学习算法之前,必须要了解一些常用数据结构的概念。 栈:一种特殊串联形式的抽象数据类型,可由链表或数组实现,通过链表或数组的栈顶(Top)指针对数…

数据结构

数据结构是对实际问题中的数据元素及相互间的联系的抽象。一般用线性表来表示常用数据结构,线性表分为顺序存储的顺序表和连式存储的链表。

常用数据结构

在学习算法之前,必须要了解一些常用数据结构的概念。

  • 栈:一种特殊串联形式的抽象数据类型,可由链表或数组实现,通过链表或数组的栈顶(Top)指针对数据进行压栈(Push)和出栈(Pop)操作,其特点是LIFO。

  • 队列:先进先出(FIFO)的线性表,一般用链表和数组来实现,只允许在后端(back or rear)插入,在前端(front)删除。

  • 数组:由相同元素的集合所组成的数据结构,存储在一块连续的内存单元,根据元素的索引可以计算出该元素对应的存储地址。

  • 链表:由一连串节点组成,每个节点包含任意的实例数据和一个或两个用来指向下一个/上一个节点位置的链接。

  • 树:实现抽象数据类型的数据结构,如:二叉树、霍夫曼树。

  • 图:表示物件与物件之间的关系,图论的基本研究对象。

  • 堆:是计算机科学中一种特别的树状数据结构,也是一种特殊的二叉树。

  • 散列表:根据键(key)直接访问内存存储位置的一种数据结构,通过计算一个关于键值的函数,将所需查询的数据映射到表中的一个位置来访问记录,映射函数叫做散列函数,存放记录的数组叫散列表(散列函数和哈希冲突是实现散列表最重要的两个环节)。

算法

所谓算法,就是解决问题方案的准确而完整的描述。

运算操作

为了完成各种运算,计算机提供了一套最基本的功能操作:

  1. 算术运算:加、减、乘、除;
  2. 逻辑运算:与、或、非;
  3. 比较运算:大于,小于,等于,不等于;
  4. 数据转送:输入、输出,赋值。

算法分析的重要指标

  1. 时间复杂度:指算法运行所需要的时间,即算法的时间代价。
  2. 空间复杂度:对一个算法在运行过程中所需要的辅助存储空间大小的度量。

算法面试题

介绍完理论,可以开始实践了,以下算法题目有很多可以在剑指Offer一书中找到,此文以及所涉及的题目仅供参考,不同的题目有不同的解答方式,切勿死记,一定要基于空间复杂度和时间复杂度再选择对应的算法。此文部分题目提供了部分参考代码,完整参考代码请点击此处查看。

  • 算术运算题

    1. 质数
      • 大于1的自然数中,只能被1和自身整除的自然数(不要考虑1),质数判断的参考实现如下:
    bool isPrime(unsigned n) {
        for (int i = 2; i < n; i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }
    
    1. 丑数
      • 只能被2、3和5整除的正整数数,1是第一个丑数,丑数判断的参考实现如下:
      bool isUgly(int n) {
          if (n >= 1) {
              while (n % 2 == 0) {
                  n = n / 2;
              }
              while (n % 3 == 0) {
                  n = n / 3;
              }
              while (n % 5 == 0) {
                  n = n / 5;
              }
              if (n == 1) {
                  return true;
              }
          }
          return false;
      }
      
    2. 斐波那契数列
      • 从0和1开始,之后的数就是前两个数的和,参考实现如下:
      long long fibonacciSequence(unsigned n) {
      
          int result[] = {0, 1};
          if (n < 2) {
              return result[n];
          }
          long long firstValue = 0;
          long long secondValue = 1;
          long long sum = 0;
          for (int i = 2; i < n; i++) { // n以内的自然数
              sum += firstValue + secondValue;
              firstValue = secondValue;
              secondValue = sum;
          }
          return sum;
      }
      
  • 排序

    1. 冒泡排序(平均复杂度 O(n^2) 且稳定)
      • 每趟确定一个最小值,把最小值往上冒,参考实现如下:
      void bubbleSort(int *a, int len) {
          if (a == nullptr || len < 1) {
              return;
          }
          for (int i = 0; i < len-1; i++) {
              for (int j = 0; j < len-1-i; j++) {
                  if (a[j] > a[j+1]) {
                      swap(a[j], a[j+1]);
                  }
              }
          }
      }
      
    2. 选择排序(平均复杂度 O(n^2) 且稳定)
      • 选择一个数字,然后与其余数字逐个比较,小于该数字则交换,参考实现如下:
      void selectSort(int *a, int len) {
          if (a == nullptr || len < 1) {
              return;
          }
          for (int i = 0; i < len - 1; i++) {
              for (int j = i+1; j < len; j++) {
                  if (a[i] > a[j]) {
                      swap(a[i], a[j]);
                  }
              }
          }
      }
      
    3. 插入排序(平均复杂度 O(n^2) 且稳定)
      • 从头开始,选择一个数与前面排好序的数比较,把该数插入到合适的位置,参考实现如下:
      void insertSort(int *a, int len) {
          if (a == nullptr || len < 1) {
              return;
          }
          for (int i = 0; i < len; i++) {
              for (int j = i+1; j > 0; j--) {
                  if (a[j] < a[j-1]) {
                      swap(a[j], a[j-1]);
                  }
              }
          }
      }
      
    4. 快速排序(平均复杂度 O(nlogn) 不稳定)
      • 核心实现是,选择一个数作为基准值,大于该值的放在右边,小于该值的放在左边,最后把该值放在左右分割的位置,递归参考实现如下:
      void IVSort::quickSort(int *a, int len) { // step 1
          if (a == nullptr || len < 1) {
              return;
          }
          quickSortImp(a, 0, len-1);
      }
      
      void quickSortImp(int *a, int start, int end) { // step 2
          if (start >= end) {
              return;
          }
          int index = quickSortImpCore(a, start, end);
          if (index > start) {
              end = index-1;
              quickSortImp(a, start, end);
          }
          if (index < end) {
              start = index+1;
              quickSortImp(a, start, end);
          }
      }
      // 快速排序核心实现
      int quickSortImpCore(int *a, int start, int end) { step 3
          int index = start + 1;
          // 默认选第一个作为基本值
          for (int i = index; i <= end; i++) {
              if (a[start] > a[i]) {
                  if (index != i) {
                      swap(a[index], a[i]);
                  }
                  ++index;
              }
          }
          --index;
          swap(a[start], a[index]);
          return index;
      }
      
  • 查找

    1. 二分法(折半查找,平均复杂度O(logn))
      • 通过左右索引,不断缩小查找区域,适用于已排序数列,参考实现如下:
      bool binarySearch(int *a, int len, int target) {
          if (a != nullptr && len > 0) {
              int start = 0;
              int end = len - 1;
              while (start < end) {
                  int mid = (start + end)/2;
                  if (a[mid] > target) {
                      end = mid-1;
                  }else if (a[mid] < target) {
                      start = mid + 1;
                  }else {
                      return true;
                  }
              }
          }
          return false;
      }
      

鉴于代码量大会导致篇幅过长,不方便阅读,以下题目不在贴出参考代码,有需要可点击此处查看。

  • 字符串

    1. 字符串翻转
    2. 字符串转数字
    3. 字符串替换
  • 数组

    1. 找出重复的数
    2. 二维数组的查找
    3. 旋转数组中最小的数
    4. 数组中出现超过一半的数字
    5. 数组中第K大的数
    6. 有序数组中某个数出现的次数
    7. 调整奇数位位于偶数位前面
  • 链表

    1. 删除指定节点
    2. 删除重复节点
    3. 倒数第K个节点
    4. 中间节点
    5. 入口节点
    6. 翻转链表
    7. 合并两个链表
    8. 两个链表的首个公共节点
  • 树

    1. 前序遍历(递归和非递归)
    2. 中序遍历(递归和非递归)
    3. 后序遍历(递归和非递归)
    4. 根据前序和中序构建二叉树
    5. 根据中序和后序构建二叉树
    6. 中序遍历的下一个节点
    7. 翻转二叉树
    8. 树的深度
    9. 根据中序遍历判断是否是二叉搜索树(BST:Binary Search Tree)
    10. 从上到下打印二叉树

总结

对于从事编程工作的朋友来说,数据结构与算法是编程的基础(这里是网络篇和iOS篇),也是很多大厂面试的必考题,熟悉算法不仅仅是为了应付面试,算法题巧妙的解决思路有助于帮助我们提高代码的执行效率和丰富解决问题的思路,实乃防治老年痴呆必备之良药,望按需取之,另:各位若有更好的解决思路,欢迎一起学习交流。


PS:此文给出的参考代码,不一定是最佳的答案,且以上代码都是基于Xcode环境编写,并提供了一些简单的Test Case,没有完全覆盖,大家可以自行编写case测试,若发现有任何Bug,可通过新浪“Joelixy_”和GitHub及时与我联系,我会在第一时间修正,谢谢!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/12882.html

赞 (0)

相关推荐

  • 如何将必应设为浏览器主页_必应APP

    如何将必应设为浏览器主页_必应APP加入NewBing的Ai聊天机器人,个人感觉比ChatGPT更详细一些,会帮你把答案来源标出来,很方便!

    2023-08-23
    130
  • Python初学者如何开始学习Python语言

    Python初学者如何开始学习Python语言随着计算机技术的发展,越来越多的人开始学习编程语言。而Python,作为一门简单易学,功能强大的编程语言,吸引了越来越多的初学者。那么,作为Python初学者,如何开始学习这门语言呢?下面,我们将从多个方面为大家详细阐述。

    2023-12-22
    112
  • 随笔记录-_随笔笔记怎么写

    随笔记录-_随笔笔记怎么写我之前用sqlserver连过很多人的数据库,后来我怕登陆的时候登陆错了,想清楚一下连接那里的默认记录,后来在网上找过许多方法都不行,后来误打误撞找到了方法,大家可以试一下下边的方法: 有的直接放在U

    2023-02-05
    148
  • Python Widget Digit,打造高效数字化界面

    Python Widget Digit,打造高效数字化界面在现代社会,数字化已成为各行各业的趋势,需要我们处理数字化信息的频率越来越高。数字处理和显示是我们日常工作的重点,因此,有一个高效的数字化界面是非常重要的。Python Widget Digit能够帮助我们快速、轻松地构建一个高效的数字化界面。

    2024-04-03
    69
  • Python DataFrame遍历

    Python DataFrame遍历在数据分析、挖掘与建模中,DataFrame 是不可或缺的一种数据结构。然而,在进行数据处理时,往往需要对 DataFrame 进行遍历操作。本文将从多个方面介绍 Python 中对 DataFrame 进行遍历的方法。

    2024-07-12
    40
  • Python安装配置指南

    Python安装配置指南Python有多个版本,不同版本的语法支持和工具包不尽相同。在安装之前,需要选择需要的Python版本。

    2024-02-19
    91
  • 面试官看上你的表现_数据分析sql面试必会6题经典

    面试官看上你的表现_数据分析sql面试必会6题经典和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。 连接层:最上层是一些客户端和连…

    2023-04-03
    140
  • Android组件化开发思想与实践[亲测有效]

    Android组件化开发思想与实践[亲测有效]项目按功能拆分成功若干个组件,每个组件负责相应的功能,如login、pay、live。组件化与模块化类似,但不同的是模块化是以业务为导向,组件化是以功能为导向。组件化的颗粒度更细,一个模块里可能包含多个组件。实际开发中一般是模块化与组件化相结合的方式。 (1)提高复用性避免重复…

    2023-08-17
    146

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注