数据结构常见笔试题【备战春招秋招】

前言

        该文章记录了近些年常见的数据结构相关笔试题不涉及繁琐算法题逻辑题

        该文章适合有学习过C语言与数据结构的朋友阅读,主要是针对本科应届生。

链表

        该部分较为基础,主要出现在一些中小公司的笔试大题。有时会在面试中询问解决方案。该部分所有链表均为单向有头链表,结点结构如下:

typedef struct Node {
    int data;    //数据域,报错数据
    struct Node *next;    //指针域,指向下一个结点的地址
}node_t;

链表的排序

方案一、将原有链表打断,重新按顺序插入

//有序插入  将node结点按顺序插入到链表中
void insert_linklist_order(node_t *head, node_t *node)
{
    node_t *p = NULL;
    for (p = head; p != NULL; p = p->next) {
        if (p->next == NULL || node->data < p->next->data) {
            node->next = p->next;
            p->next = node;
            break;
        }
    }
}

//给链表排序
void sort_linklist(node_t* head)
{
    if (head == NULL) {
        return;
    }
    node_t *p = head->next;   //将链表打断,p指向链表后面部分
    head->next = NULL;
    
    while (p != NULL) {       //依次将后面的部分按顺序重新插入到链表中
        node_t *tmp = p->next;
        insert_linklist_order(head, p);
        p = tmp;
    }
}

方案二、只交换链表内数据,不更换指针域,此处以冒泡排序举例

void sort_linklist(node_t *head)
{
    if (head == NULL) {
        return;    
    }
    
    node_t *end = NULL;   //保存尾值
    for (node_t *i = head->next; i->next != NULL; i = i->next) {
        for (node_t *j = list->next; j->next != end; j = j->next) {
            if (j->data > j->next->data) {
                int tmp = j->data;
                j->data = j->next->data;
                j->next->data = tmp;
            }
        }
        end = j;    //更新尾值,提高性能
    }
}

链表的逆序

        将链表看成两个链表,第一个链表只有头结点与第一个元素,第二个链表从第二个元素开始,将第二个链表的元素以头插法的形式依次插入到第一个链表中即可

void reverse_linklist(node_t *head)
{
    if (head == NULL || head->next == NULL) {
        return;
    }

    //保存第二个有效数据的地址
    node_t *p = head->next->next;

    //把第一个有效数据的next置为NULL    拆分成两个链表操作
    head->next->next = NULL;

    //将p看成链表的头结点,将p中元素依次头插进list中
    node_t *tmp = NULL;
    while (p != NULL) {
        tmp = p;
        p = p->next;
        tmp->next = head->next;
        head->next = tmp;
    }
    return;
}

两个有序链表合并成一个有序链表

node_t* and_linklist(node_t *list1, node_t *list2)
{
    if (list1 == NULL) {
        return list2;
    }
    if (list2 == NULL) {
        return list1;
    }

    node_t *p1 = list1->next;
    node_t *p2 = list2->next;
    node_t *tmp = list;
    free(list2);    //以list1为结点,释放list2
    
    while (1) {
        if (p1->data > p2->data) {    //比较两个链表每个结点大小
            tmp->next = p2;
            p2 = p2->next;
            tmp = tmp->next;
            if (p2 == NULL) {        //list2结点遍历结束后连接list1后所有数据退出
                tmp->next = p1;
                break;
            }
        }
        else {
            tmp->next = p1;
            p1 = p1->next;
            tmp = tmp->next;
            if (p1 == NULL) {
                tmp->next = p2;
                break;
            }
        }
    }
    return list1;    //返回合并后链表首地址
}

判断链表是否成环

使用快慢指针,有环必相遇。

int if_loop(node_t *head)
{
    node_t *quick = head;
    node_t *slow = head;

    if (head == NULL || head->next == NULL) {
        return 0;
    }
    
    do 
    {
        quick = quick->next->next;  //快指针每次前进两格
        slow = slow->next;    //慢指针每次前进一格
        if (quick == slow) {
            return 1;    //相遇表示成环
        }
    }while(quick);
    
    return 0;
}

已知f为单链表的表头指针,链表中存储的都是整形数据,试写出求所有整数的平均值的递归算法

// 递归函数计算链表中所有整数的平均值
float calculateAverage(node_t* head, int count, int sum) 
{
    if (head == NULL) {
        return (float)sum / count;
    } 
    else {
        sum += head->data;
        count++;
        return calculateAverage(head->next, count, sum);
    }
}

二叉树

        该部分相对来说较为少见,一般出现在大厂的笔试大题中,基础部分在笔试中可能也要求编写。

二叉树的前中后序遍历

//二叉树结点定义
struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

//前序遍历
void preOrder(struct Node* root) {
    if (root == NULL) {
        return;
    }
    printf("%d ", root->data);
    preOrder(root->left);
    preOrder(root->right);
}

//中序遍历
void inOrder(struct Node* root) {
    if (root == NULL) {
        return;
    }
    inOrder(root->left);
    printf("%d ", root->data);
    inOrder(root->right);
}

//后序遍历
void postOrder(struct Node* root) {
    if (root == NULL) {
        return;
    }
    postOrder(root->left);
    postOrder(root->right);
    printf("%d ", root->data);
}

定义二叉树数据结构并编写C函数foo():遍历计算二叉树所有节点及其叶子节点的数值之和

// 定义二叉树节点结构
struct Node 
{
    int data;
    struct Node* left;
    struct Node* right;
};

// 返回数值之和,参数为二叉树根节点
int foo(struct Node* root) 
{
    if (root == NULL) {
        return 0;
    }
    return root->data + foo(root->left) + foo(root->right);
}

查找与排序算法

        该部分非常常见,各种级别的公司中都会出现1~2道算法题目。没有记录过于少见或难度较高的算法题。

写一个函数找出一个整数数组中,第二大的数

方案一、使用INT_MIN宏(不推荐)

int findSecondLargest(int *arr, int size) 
{
    assert((arr != NULL));
    int first = -2147483648;   //int最小的数  可以用INT_MIN代替
    int second = -2147483648;

    for (int i = 1; i < size; i++) {
        if (arr[i] > first) {
            second = first;
            first = arr[i];
        } 
        else if (arr[i] > second && arr[i] != first) {
            second = arr[i];
        }
    }
    assert((second != -2147483648));
    return second;
}

方案二、不使用INT_MIN宏(推荐)

int findSecondLargest(int *arr, int size) {
    int firstLargest = arr[0];
    int secondLargest = arr[0];

    for (int i = 1; i < size; i++) {
        if (arr[i] > firstLargest) {
            secondLargest = firstLargest;
            firstLargest = arr[i];
        } 
        else if (arr[i] > secondLargest && arr[i] != firstLargest) {
            secondLargest = arr[i];
        }
    }

    return secondLargest;
}

希尔排序(shell)

用于排序,适合新人背诵,切记不要在排序题中写冒泡或选择排序。

void shellSort(int *buf, int size)
{
    int gap = 0;    //表示增量变化
    int temp = 0;    //记录当前待排序的数据
    int i = 0, j = 0;
    
    //增量依次变小
    for (gap = size / 2; gap > 0; gap /= 2) {
        //直接插入排序
        for (i = gap; i < size; ++i) {
            temp = buf[i];
            for (j = i - gap; j >= 0 && buf[j] > temp; j -= gap) {
                buf[j + gap] = buf[j];
            }
            buf[j + gap] = temp;
        }
    }
    return;
}

快速排序(快排)

用于排序,适合进阶一点的人使用,最少要掌握其递归的思想

/*
*    buf为数组首地址
*    low为待排序数组的最小下标
*    high为待排序数组的最大下标
*/
void quickSort(int *buf, int low, int high)
{
    int first = low;
    int last = high;

    int key = buf[first];    //设置第一个数为key

    if (low >= high) {  //找到中介这一轮判断结束
        return ;
    }

    //使用first与last判断,比key大的放左边,比key小的放右边
    while (first < last) {
        //从右往左,找到比key小的数,若比key大则继续往左
        while (first < last && buf[last] >= ket) {  
            --last;
        }
        //将比key小的数放到buf[first]位置
        buf[first] = buf[last];

        //从左往右,找到比key大的数,若比key小则继续往右
        while (first < last && buf[last] <= ket) {  
            ++first;
        }
        //将比key大的数放到buf[last]位置
        buf[last] = buf[first];
    }

    //first和last相遇 或 first > last 表示该轮结束,将备份的key值赋给buf[first]
    buf[first] = key;

    quickSort(buf, low, first-1);   //对前一半进行排序
    quickSort(buf, first+1, high);  //对后一半进行排序
}

堆排序

用于排序,对思维要求较高,建议有能力的同学背诵,笔试题写此排序算法将非常亮眼

// 调整堆,使以节点i为根的子树成为最大堆
void heapify(int arr[], int n, int i) {
    int largest = i; // 初始化最大值为根节点
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    // 如果左子节点比根节点大
    if (left < n && arr[left] > arr[largest]) {
        largest = left;
    }

    // 如果右子节点比根节点大
    if (right < n && arr[right] > arr[largest]) {
        largest = right;
    }

    // 如果最大值不是根节点,交换根节点和最大值节点
    if (largest != i) {
        int temp = arr[i];
        arr[i] = arr[largest];
        arr[largest] = temp;

        // 递归调整交换后的子树
        heapify(arr, n, largest);
    }
}

// 堆排序
void heapSort(int arr[], int n) {
    // 构建最大堆
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }

    // 逐个取出堆顶元素,再调整堆
    for (int i = n - 1; i > 0; i--) {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;

        heapify(arr, i, 0);
    }
}

几种排序的复杂度与稳定性

建议至少选择一种排序算法和对应复杂度背诵

(C++) 给定一个n个元素的有序(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标存在则返回下标,否则返回-1。使用二分法。函数原型:int search(vector<int>& nums, int target);

int search(vector<int>& nums, int target) 
{
    int left = 0;
    int right = nums.size() - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;

        if (nums[mid] == target) {
            return mid;
        } 
        else if (nums[mid] < target) {
            left = mid + 1;
        } 
        else {
            right = mid - 1;
        }
    }

    return -1;
}

结语

        编写该文章目的主要为想从事相关工作的同学找到一份好的工作,以上题目在笔试中经常出现,如果有在外面试的朋友发现有更常见更经典的题目也可以私信告知,后续也会更新到博客当中。

        如果有朋友想系统的学习嵌入式相关知识,从事相关的行业,可以私信,有一些经典的电子档书籍资料和开源网课学习链接

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 把一个链表反向,递归,非递归都写一遍。 1.试编写3个函数实现   (1)建立一个双向链表   (2)插入一个节点   (3)删除一个节点 2.自己定义数据结构,写出程序:二叉树的前序遍历。 3.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。 4.下面哪种排序法对12354最快 a quick sort b.buble sort c.merge sort 5.哪种结构,平均来讲,获取一个值最快 a. binary tree b. hash table c. stack 6.一个二叉树的三种遍历方法的输出结果 7.链表按升序打印每打印完一个节点就将该节点从链表中删除 8.选择一种算法来整理出一个链接表。你为什么要选择这种方法?现在用o(n)时间来做。 9. 用一种算法在一个循环的链接表里插入一个节点,但不得穿越链接表。    10.给两个变量,如何找出一个带环单链表中是什么地方出现环的? 11.哈希表和数组的定义,区别,优缺点。 12.链接表和数组之间的区别是什么? 任选一门语言,当场定义二叉排序树数据结构,写出两个函数:初始化,删除一个节点,20分钟 13. 递归的折半查找算法[不限语言] 14. 解释一下什么是B+树,如何实现B+树的查找和插入.(用图示) 15.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。 13.排序方法比较 (intel) 排序方法 平均时间 最坏时间 辅助存储 直接插入排序 O(N2) O(N2) O(1) 起泡排序 O(N2) O(N2) O(1) 快速排序 O(Nlog2N) O(N2) O(Nlog2N) 简单选择排序 O(N2) O(N2) O(1) 堆排序 O(Nlog2N) O(Nlog2N) O(1) 归并排序 O(Nlog2N) O(Nlog2N) O(n) 基数排序 O(d(n+radix)) O(d(n+radix)) O(radix) 17.一个链表的操作,注意代码的健壮和安全性。要求: (1)增加一个元素; (2)获得头元素; (3)弹出头元素(获得值并删除)。 18.内排序算法 19.折半查找的复杂度,证明 20.sizeof()和strlen()的使用. 21.顺序存储结构的优点,散列法的思想是什么? 22.汉罗塔算法,不能递归... 23.一个链表的结点结构 struct Node { int data ; Node *next ; }; typedef struct Node Node ; (1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel) (2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表 依然有序。 (3)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表 依然有序,这次要求用递归方法进行。 ( Autodesk) 24.编最优化Bubble(int *pIntArray,int L),要求:交换元素不能用临时变量,如果有序需要最优。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值