无极4注册登录网址《Q31779871 》软件工程职位的面试不同于大多数其他职业的面试过程。有些部分是相似的,因为你可能会被问到你以前的工作和教育,并被问到能力问题来测试你的情景判断和经验。然而,面试官在面试过程中最看重的是你的编程能力。你需要证明你能快速思考并解决呈现给你的技术或逻辑问题。这通常包括在电话或视频通话中通过代码进行交谈,或在白板上写下解决方案。您通常可以使用任何流行的编程语言(如Java或c++)编写代码。为了帮助你进行软件工程面试,无极4注册登录网址我们将使用Python编程语言,因为它易于阅读,而且语法简单,非常适合面试。
在软件工程面试的这些技术阶段,几乎可以保证您会被问到关于数据结构的问题,如堆栈、树和哈希映射。您可能还会被问到关于算法的问题,这些算法通常通过使用特定的数据结构得到理想的解决。在这里,我们将研究一种这样的数据结构——链表。它们是什么,为什么使用它们,以及如何实现它们?
让我们从基础开始。链表到底是什么?链表是连接节点的链,无极四荣耀注册节点是由一个值和一个指向另一个节点的指针组成的对象。下面的图表有助于可视化这个概念。
定义类
要实现链表,首先要做的是创建一个Node类,它将保存节点的数据和指向链中下一个节点的指针。
类节点:
(self, data=None, next=None):
自我。=数据
自我。明年=
由于链表本身就是一个节点链,因此要存储链表,我们基本上只需要对第一个节点的引用。下面创建了LinkedList类来存储列表的头。
LinkedList类:
def init(自我):
自我。头=没有
打印链表
链表所需的最基本功能之一是打印它的能力。在测试其他操作是否按预期执行时,打印链表的值将特别有用。可以使用迭代方法遍历列表的每个节点并打印出它们的值。
def print_list(自我):
节点= self.head
而节点!=无:
打印(node.data)
节点= node.next
插入节点
另外两个关键函数是向链表插入节点和从链表中删除节点。让我们从插入节点开始。节点可以插入到列表的头、尾或中间的某个位置。这些选项中最简单的是最前面的选项。
def insert_at_head(自我、数据):
自我。head = Node(data, self。head)
在这一行中,我们创建了一个带有传入数据的新节点对象,它指向当前的头节点。然后,我们的新节点被分配为链表的头节点。所以我们的链表现在以新节点为首,它指向旧的头,在那里链表继续像以前一样。
在列表的尾部插入一个节点需要更多的工作。
def insert_at_tail(自我、数据):
如果列表为空,则作为头节点插入
如果self.head = =没有:
自我。head =节点(数据,无)
其他:
current_node = self.head
而current_node。下一个! =没有:
current_node = current_node.next
current_node。next = Node(data, None)
如果列表当前是空的,那么尾节点和头节点是相同的,因此我们插入一个节点作为列表的头节点。如果列表不是空的,我们遍历节点,直到到达当前尾部。然后将这个尾节点指向新节点,从而将其附加到列表的末尾。
我们还可以在特定索引(列表中的数字位置)处插入节点。为此,我们再次检查一个空列表,在这种情况下,我们插入作为头节点。如果列表不是空的,我们希望遍历列表中的节点,直到到达所需的索引位置。如果到达索引之前的列表末尾,则位置超出了范
def insert_at_index(self, data, index):
如果列表为空,则作为头节点插入
如果self.head = =没有:
自我。head =节点(数据,无)
返回
我= 0
current_node = self.head
虽然我<指数:
如果索引超出范围,请不要插入
如果current_node.next = =没有:
返回假
current_node = current_node.next
我+ = 1
current_node。下一个节点(current_node =。数据、current_node.next)
current_node。=数据
删除节点
对于删除特定索引处的节点,方法类似于插入。我们再次遍历链表,直到我们想要的索引位置,但是这一次除了跟踪当前节点外,我们还需要跟踪上一个节点。这是因为一旦达到了所需的索引位置,我们就将上一个节点设置为指向当前节点的指针。这实际上使链跳过了当前节点,并且在没有任何东西再引用它时将其删除。
def delete_at_index(自我,指数):
如果索引为负,请不要删除
如果指数< 0:
返回假
我= 0
prev_node =没有
current_node = self.head
虽然我<指数:
如果索引超出范围,不要删除
如果current_node.next = =没有:
返回假
prev_node = current_node
current_node = current_node.next
我+ = 1
如果prev_node = =没有:
自我。头= current_node.next
其他:
prev_node。下一个= current_node.next
测试
下面是一些测试示例,无极四注册以确保我们的链表按预期执行。在编码数据结构时,一定要考虑到边界情况,比如非正整数和超出范围的值。
list1 = LinkedList ()
打印(“空列表:“)
list1.print_list ()
打印(“头部插入节点:”)
list1.insert_at_head(“你好”)
list1.print_list ()
打印(“头部插入节点:”)
list1.insert_at_head (5)
list1.print_list ()
打印(“节点插入尾部:”)
list1.insert_at_tail(结束)
list1.print_list ()
打印(“插入到索引2的节点:”)
2) list1.insert_at_index(11日
list1.print_list ()
打印(“插入索引0处的节点:”)
list1.insert_at_index(“开始”,0)
list1.print_list ()
打印(“节点插入到范围之外:”)
。insert_at_index(‘不应该看到这个’,100)
list1.print_list ()
打印(“删除索引1的节点:”)
list1.delete_at_index (1)
list1.print_list ()
为什么链表?
既然我们已经了解了链表的基本知识,那么使用它们的原因就值得考虑了。它的主要优点是,它是一种动态数据结构,只有在需要时才分配内存,而数组的大小是预先确定的。它们对于实现堆栈和队列等进一步的数据结构也很有用,在这些结构中,对头节点和尾节点的访问是有价值的。
使用链表的一个缺点是,通过存储每个节点的数据和指针,会使用额外的内存。访问列表中的特定节点可能需要一次完整遍历,这是时间复杂度方面的另一个缺点。
在软件工程面试中,在评估代码时,时间复杂度的话题经常会被作为一个重要的考虑因素。链表的插入和删除有不同的时间复杂度。在链表顶部插入只需O(1),但是遍历一个链表需要O(n)时间,并且需要在特定索引处插入或删除、节点查找和更复杂的操作。
示例面试问题
面试中你可能会被问到的一个问题是如何倒转链表。试着先自己解决这个问题,然后用下面的解决方案检查一下你是否使用了同样的方法。
def反向(自我、节点):
如果node==None或node。下一个= =没有:
自我。头=节点
返回
self.reverse (node.next)
node.next。下一个=节点
节点。下一个=没有
处理这类问题有不同的方法——所以如果你的方法不同,也不用担心。这里我们用的是递归解。我们传入一个节点,在初始函数调用中,它将是列表的头。然后是基本情况,检查是否到达列表的末尾(最后一个节点),在这种情况下,将头节点作为传入的节点设置——这本质上是对单个节点进行“反转”。
如果我们还没有到达基本情况,那么我们将进行递归调用,它将从下一个节点开始反转列表。最后两行实际上是相反的,我们将节点的下一个节点设置为指向我们的节点,将节点设置为指向None,因为我们位于列表的末尾。
什么下一个
扩展你的链表知识的一些想法:
到目前为止,我们使用的是一个只在一个方向上连接的链表——这被称为单链表。还有一种称为双链表的数据结构,其中每个节点都有指向下一个节点和前一个节点的指针。要实现这个功能,您需要添加什么?
在类中存储链表的其他属性可能会很有用,例如尾节点的指针或长度计数器。这些对我们的功能有什么帮助?
如果我们想要查找链表中的某个节点,这个操作的最佳情况和最差情况的时间复杂度是多少?
请尽快查看另一篇关于数据结构和算法的文章,这可能是你下一次软件工程面试成功的关键!