常见的数据结构:8种数据结构及面试问题

常见数据结构:
数组


队列

链表

字典树
散列表

数组

数组是最简单,使用呢最广泛的数据结构。栈,队列等其他数据结构均由数据结构演变而来,

基本操作

insert
get
delete
size

面试常见问题

1.寻找数组中第二小的元素

解决方案:1.直接对数字进行排序,排序后区第二小的元素,这种时间复杂度是O(nlogn)
2.更好的方案是,由于只要一个元素,我们只需要遍历两遍,每次查找最小的元素,时间复杂度为O(n)
3.还有最好的方案,只需要遍历一遍,定义一个两位数大小的数组,一次遍历找到最小的两个数。

代码:

void find_min_two(int a[10],int size)
{
	int first_min = INT_FAST16_MAX;
	int second_min = INT_FAST16_MAX;

	for (int i = 0; i < size; i++) {
		if (a[i] < first_min) {
			second_min = first_min;
			first_min = a[i];
		}
		else if(a[i] < second_min&&a[i] >= first_min){
			second_min = a[i];
		}
	}
	cout <<"第二小的数为"<<second_min;

}

2.找到数组中第一个不重复出现的整数

解决方案:1.时间换空间,如果空间够大,且数字的大小比较小,我们可以使用**位图法**来表示该数字是否出现。时间复杂度为O(n)。
2.时间换空间,使用双重循环。时间复杂度为O(n^2).
3.更好的方法。
思路二代码:
public Integer handler(int [] arr){
        Integer position=null;
        for (int i = 0; i < arr.length; i++) {
            for (int j =0; j <arr.length ; j++) {
                if(i!=j&&arr[i]==arr[j]){
                    position=j;
                    break;
                }
                if(j==arr.length-1){
                    position=arr.length;
                }
            }
            if(position==arr.length){
                return arr[i];
            }
        }
        return null;
    }

3.合并两个有序数组

解决方案:
此方案代码:
class Solution{
    public void merge(int nums1 [], int m, int nums2 [], int n){
        int i = m-1, j = n-1, k = m+n-1;
        while(i>=0 && j>=0){
            if(nums1[i] > nums2[j])
                nums1[k--] = nums1[i--];
            else
                nums1[k--] = nums2[j--];     
        }
        //当nums1数组长度大于nums2时。不需要做什么
        //当nums2数组长度大于num1时,需要将数据复制过来
        while(j>=0){
            nums1[k--] = nums2[j--];
        }  
    }  
}

4.重新排序数组中的正值和负值,将负数放在前,正数在后,其他顺序不变。

解决方案:1.最简单的就是遇到每个堵住都把它移到数组前面,已排序的负数的后面,时间复杂度为O(n^2)
2.使用Merge Sort ,时间复杂度为O(nlogn),空间复杂度用到递归,空间复杂度为(logn)。
3.定义一个长度为n的数组,从前往后遍历一遍,将负数取出来,再从后往前遍历取出正数,时间复杂度为O(n)。空间复杂度为O(n)。

方案1:代码
方案2:代码
方案3:代码

著名的撤销操作几乎遍布了任何一个应用。这个问题的解决思路是按照将最后的状态排列在最先的顺序,在内存中存储历史工作状态,这没办法用数组实现。但有了栈,就非常方便了。

基本操作

push——从栈顶输入元素
pop——从栈顶输出元素(栈和队列都是从栈顶输出元素)
isEmpty——判断是否为空
Top——获取栈顶元素

面试常见问题

使用栈计算后缀表达式

对栈的元素进行排序(要求:不能使用其他结构,最多使用另一个栈存放临时数据)

使得栈内元素从栈底道栈顶降序。
解决方案:在一个栈中升序,则在另一个栈中降序,要保证数据的交换,还需要设置一个临时变量保存数据。
步骤:
1.当栈不为空时,获取栈顶元素,并将元素依次放入到辅助栈中。
2.当栈顶的元素大于辅助栈的栈顶元素时,将辅助栈中大于此元素的数都push到原栈中,再将临时元素(之前保存的栈顶元素),存入到辅助栈,以保证辅助栈中小的数在下面。
3.重复12,直到stack为空,最后将辅助栈中的元素反转过来。

代码:

public static void SortStack(Stack<Integer> stack){
        Stack<Integer> help = new Stack<>();
        //****************************主要是这一段
        while (!stack.isEmpty()){
            int cur = stack.pop();
            while (!help.isEmpty() && help.peek()<cur){
                stack.push(help.pop());
            }
            help.push(cur);
        }
        //*****************************后面进行反转
        System.out.println(help);
        while (!help.isEmpty()){
            stack.push(help.pop());
        }
        System.out.println(stack);
    }

判断表达式是否括号平衡
使用栈来表示队列
使用栈来表示队列

队列

与栈相似,队列是另一宗顺序存储元素的线性结构。栈与队列的最大差别在于栈是LIFO(后进先出),而队列是FIFO,即先进先出。

队列的基本操作

Enqueue()–在队列尾部插入元素
Dequeue()–移除队列头部元素
isEmpty()–判断是否为空
Top()—返回队列第一个元素

面试常见问题

使用队列表示栈

思路:模拟栈,需要两个队列,用队列的先进先出,模拟栈的先进后出,
也就是说每次取的队列的元素都是队列尾的元素,
所以我们需要将队列的前n-1个元素先存储到辅助队列中,
等取出队尾元素,后再入队列。

需要使用队列的方法来实现栈的方法:

代码:

	Queue queue1 = new Queue();//主要存放数据
    Queue queue2 = new Queue();//辅助
    public void push(Object o) {
        queue1.add(o);
    }
    public Object pop() {
        Object o = null;
        while(queue1.length()>1) {
            queue2.add(queue1.poll());
        }
        if(queue1.length()==1) {
            o = queue1.poll();
            while(queue2.length()>0) {
                queue1.add(queue2.poll());
            }
        }
        return o;
    }

队列的前k个元素倒序
使用队列生成从1到n的二进制数

链表

链表是另一个重要的线性数据结构,乍一看可能有点像数组,但在内存分配、内部结构以及数据插入和删除的基本操作方面均有所不同。

基本操作

insertAtEnd——末尾插入
isertAtHead——链表开头插入
delete——从链表中删除指定元素
deleteAtHead——删除连接列表的第一个元素
search——从链表中返回指定元素
isEmpty——如果链表为空,则返回true

常见问题

反转链表

struct ListNode *reverseList(struct ListNode* head) {
	if (head == NULL) {
		return NULL;
	}
	struct ListNode *p0 = NULL;
	struct ListNode *p1 = head;
	struct ListNode *p2 = head->next;
	while (p1 != NULL) {
		p1->next = p0;

		p0 = p1;
		p1 = p2;
		if (p2 != NULL) {
			p2 = p2->next;
		}
	}
	return p0;
}

检测链表中的循环
返回链表倒数第N个结点
删除链表中的重复项

图是一组以网络形式相互连接的节点。节点也称为顶点。 一对节点(x,y)称为边(edge),表示顶点x连接到顶点y。边可以包含权重/成本,显示从顶点x到y所需的成本。

图的存储

邻接矩阵
邻接表

图的遍历算法

广度优先算法
深度优先算法

常见面试题

实现广度和深度优先搜索
检查图是否为树
计算图的边数
找到两个顶点之间的最短路径

树形结构是一种层级式的数据结构,由顶点(节点)和连接它们的边组成。 树类似于图,但区分树和图的重要特征是树中不存在环路。

树的主要类型

N元树
平衡树
二叉树
二叉搜索树
AVL树
红黑树
2-3树

其中二叉树和二叉搜索树最常用

常见面试题

求二叉树的高度
求二叉树的高度
解决方案:1使用递归
2.后序遍历二叉树,节点最大栈长即为二叉树的高度
3.层次遍历二叉树,使用队列
方案一:代码

public class 递归 {
	class TreeNode{
		int val;
		TreeNode left;
		TreeNode right;
		public TreeNode(int value){
			this.val=value;
		}
	}
	public int getHeight(TreeNode root){
		if(root==null){
			return 0;
		}
		int leftheight=getHeight(root.left);
		int rightheight=getHeight(root.right);
		return Math.max(leftheight, rightheight)+1;
	}
 
}

在二叉树中查找第k个最大值
查找与根节点距离k的节点
在二叉树中查找给定节点的祖先节点

字典树(Trie)

字典树也称前缀树,是一种特殊的树状数据结构,对于解决字符串相关的问题非常有效,它能够提供快速的检索,主要用于搜索字典中的单词,在搜索引擎中自动提供建议,甚至被用于IP路由器。

常见面试问题

计算字典树中的总单词树
打印存储在字典树中的所有单词
使用字典树对数组的元素进行排序
使用字典树从字典中形成单词
构建T9字典(字典树+DFS)

哈希表

哈希法(Hashing)是一个用于唯一标识对象并将每个对象存储在一些预先计算的唯一索引(称为“键(key)”)中的过程。因此,对象以键值对的形式存储,这些键值对的集合被称为“字典”。可以使用键搜索每个对象。基于哈希法有很多不同的数据结构,但最常用的数据结构是哈希表。

哈希表通常使用数组实现

三列数据结构的性能取决于以下三个元素:

哈希函数
哈希表的大小
碰撞处理方法

常见面试问题

在数组中查找堆成键值对
追踪遍历的完整路径
查找数组是否是另一个数组的子集
检查给定的数组是否不想交。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值