C、数据结构知识点

1. 位运算符有:

&(按位与)、|(按位或)、^(按位异或)、~ (按位取反)。

其中,按位取反运算符是单目运算符,其余均为双目运算符。

位运算符的优先级从高到低,依次为~、&、^、|,

其中~的结合方向自右至左,且优先级高于算术运算符,其余运算符的结合方向都是自左至右,且优先级低于关系运算符。

2. 二维数组作为函数参数正确写法如下所示:

   void Func(int array[3][10]);
 
   void Func(int array[ ][10]);

因为数组的行数无关紧要,所以还可以写成如下形式:

void Func(int (*array)[10]);            // 注意 *array 需要用括号括起来。

这种形式的声明参数是一个指针,它指向具有10个元素的一维数组。因为[]的优先级比*的优先级高,故 *array 必须用括号括起来,否则变成了

void Func(int *array[10]);

这时候参数相当于是声明了一个数组,该数组有10个元素,其中每个元素都是一个指向整型对象的指针。

当使用 int** numbers 作为形参时,需要两次 new:

int** numbers = new int*[rows]; // 二维数组用到二维指针
numbers[i] = new int[columns]; //每一行开辟columns列int类型内存空间

3.用 new 新建结点

可以提供BinaryTreeNode(int x): m_nValue(x), m_pLeft(NULL), m_pRight(NULL){}形式的语句来快速创建赋值结点:BinaryTreeNode *root = new BinaryTreeNode(8);

struct BinaryTreeNode {
    int m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
   
    BinaryTreeNode(int x): m_nValue(x), m_pLeft(NULL), m_pRight(NULL){}
};

4.HashMap

class Solution {
    public:
        vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
            map<int,int>m;
            int n1=nums1.size(),n2=nums2.size();
            for(int i=0;i<n1;i++) m[nums1[i]]++;
            vector<int>res;
            for(int i=0;i<n2;i++)
            {
                if(m[nums2[i]]!=0)
                {
                    res.push_back(nums2[i]);
                    m[nums2[i]]=0;
                }
            }
            return res;
        }
};

5. 快速排序时间复杂度

为了分析快速排序的时间复杂度,请先看下面的主定理:

主定理: T [n] = aT[n/b] + f (n)
其中 a >= 1 and b > 1 是常量 并且 f (n) 是一个渐近正函数, 为了使用这个主定理,您需要考虑下列三种情况:

主定理.JPG

快速排序的每一次划分把一个 问题分解成两个子问题,其中的关系可以用下式表示:

T[n] = 2T[n/2] + O(n) 其中O(n)为PARTITION()的时间复杂度,对比主定理,

T [n] = aT[n/b] + f (n)

我们的快速排序中:a = 2, b = 2, f(n) = O(n)

快排时间复杂度.JPG

那么为什么还有最坏情况呢?

考虑如下极端情况,

T[n] = T[n-1] + T[1] + O(n),
问题来了,这一次的划分白玩了,划分之后一边是一个,一边是n-1个,这种极端情况的时间复杂度就是O(n2).

排序算法中的快速排序的时间复杂度即 O(n log n),它通过平均时间复杂度为 O(log n)的算法将数组中的一个元素放在正确的地方,而它需要放置 n 个元素,所以时间复杂度既 O(n log n)。

5. 虚函数

  • 如果没有使用关键字 virtual,程序将根据引用类型或指针类型选择方法
  • 如果使用了关键字 virtual,程序将根据引用或指针指向的对象的类型来选择方法

6. http 的 POST 和 GET 有什么区别?

根据 HTTP 协议的定义 GET 类型的请求是幂等的, 而 POST 请求是有副作用的, 也就是说 GET 用于获取一些资源, 而 POST 用于改变一些资源, 这可能会创建新的资源或更新已有的资源.

POST 请求比 GET 请求更加的安全, 因为你不会把信息添加到 URL 上的查询字符串上. 所以使用 GET 来收集密码或者一些敏感信息并不是什么好主意.

最后, POST 请求比 GET 请求也可以传输更多的信息.

7. 什么是 Binary search tree, 它的时间复杂度是多少?

二叉搜索树是一棵以二叉树来组织的, 它搜索的时间复杂度 O(h)O(h) 与树的高度成正比, 最坏的运行时间是 Θ(lgn).

8. static作用?

(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;

(2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;

(3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

(4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;

(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。

9. 插入排序

一种改进方法,可以将找位置和移位合并起来,每次a[i]先和a[i-1]进行比较,如果a[i]>a[i-1]说明已经有序不需要找,反之,将大于a[i]的元素一边进行后移,一边查找插入位置,代码如下:

void insertSort2(int a[], int length) {
    for (int i = 1; i < length; i++) {
        int temp = a[i];
        //将找位置与数据后移合并
        if (a[i] < a[i - 1]) {
            int j;
            for (j = i-1; j >= 0 && a[j] > temp; j--) {
                a[j+1] = a[j];
            }
            a[j+1] = temp;
        }
    }
}

我在网上也看到过另一种改进方法,将数据后移用交换函数替代,这样写代码十分简洁:

void insertSort3(int a[], int length) {
    for (int i = 1; i < length; i++) {
        for (int j = i-1; j >= 0 && a[j] > a[j+1]; j--) {
            //用交换代替数据后移
            swap(a[j], a[j+1]);
        }
    }
}

10. 冒泡排序

当我们需要排序的数组基本有序时,上面的代码还会做出很多不必要的查找判断,降低了代码的执行效率。下面我们进行第一步优化,我们先定义一个标志flag,用来判断本次排序中是否发生交换,如果没有发生交换,说明排序已经完成,我们不需要再做不必要的循环判断,代码为:

void bubbleSort(int array[], int length) {
    bool flag = true;//判断是否发生交换
    while (flag) {
        flag = false;
        for (int j = 1; j < length; j++) {
            if (array[j-1] > array[j]) {
                swap(array[j-1], array[j]);
                flag = true;
            }
        }
        length --;
    }
}

再做进一步优化,如果有数组前面几个是无序的,而后面的元素都已经是有序的,那我们就可以记录下无序的位置,下次排序判断时,只需要从数组头部遍历到该位置就可以了,这样可以省去遍历后面的元素,提高了代码的执行效率。代码为:

void bubbleSort(int array[], int length)
{
    int flag = length;
    while (flag > 0) {
        int k = flag;
        flag = 0;
        for (int j = 1; j < k; j++) {
            if (array[j - 1] > array[j]) {
                swap(array[j - 1], array[j]);
                flag = j;
            }
        }
    }
}

该方法和第二种的区别就是:先判断有没有交换,若有交换我也只遍历无序的区域。

11.快速排序

void quickSortRecursive(int array[], int start, int end) {
    if (start >= end)
        return;
    //从数列中挑出一个元素,称为"基准"。
    int mid = array[end];
    int left = start;
    int right = end - 1;
    while (left < right) {
        //从左开始找,找到大于等于 mid 的数停止。
        while (array[left] < mid && left < right) left++;
        //从右开始找,找到小于 mid 的数停止。
        while (array[right] >= mid && right > left) right--;
        //交换left和right位置的数
        swap(array[left], array[right]);
    }
    //使 left 位置数小于它左边的数,大于它右边的数。
    if (array[left] >= array[end])
        swap(array[left], array[end]);
    else
        left++;
    // 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序
    quickSortRecursive(array, start, left - 1);
    quickSortRecursive(array, left + 1, end);
}

12. 选择排序

//选择排序  平均时间复杂度O(n^2) 额外空间复杂度O(1)
void selectionSort(int array[], int length) {
    int i, j, min;
    for (i = 0; i < length; i++) {
        //找到最小元素存放到起始位置。
        min = i;
        for (j = i + 1; j < length; j++)
            if (array[j] < array[min])
                min = j;
        swap(array[i], array[min]);
    }
}

13. 堆排序

void max_heapify(int arr[], int start, int end) {
    //建立父節點指標和子節點指標
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end) { //若子節點指標在範圍內才做比較
        if (son + 1 <= end && arr[son] < arr[son + 1]) //先比較兩個子節點大小,選擇最大的
            son++;
        if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
            return;
        else { //否則交換父子內容再繼續子節點和孫節點比較
            swap(&arr[dad], &arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}
 
void heap_sort(int arr[], int len) {
    int i;
   
    //初始化,i從最後一個父節點開始調整
    for (i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
   
    //先將第一個元素和已排好元素前一位做交換,再從新調整,直到排序完畢
    for (i = len - 1; i > 0; i--) {
        swap(&arr[0], &arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}

推荐阅读更多精彩内容

  • 前言 查找和排序算法是算法的入门知识,其经典思想可以用于很多算法当中。因为其实现代码较短,应用较常见。所以在面试中...
    程序猿之路阅读 2,361评论 1 28
  • 前言 把《C++ Primer》读薄系列笔记全集。 目录 第I部分:C++基础 开始学习C++ 变量和基本类型 字...
    尤汐_Jennica阅读 5,732评论 1 43
  • 3月31号,北京,晴 以前总听孔子的论语,以为核心是“仁和义”今天听了熊毅的解读才知道是“礼”而且儒家思想是封建阶...
    博峰庸者阅读 415评论 1 50
  • 今天早上出宿舍门的时候和宿舍一个同学一起出去的,后来看到她的帽子,挺可爱的,也有点搞笑,于是我就先说了句:“这个帽...
    汲晌阅读 151评论 0 1
  • 之前关于Android写过《推荐给Android开发者的抢手书单》和《推荐给Android开发者的七本图灵书》,关...
    图灵教育阅读 541评论 2 6