目录
● 手写代码:一个单向链表,给出头结点,找出倒数第N个结点,要求O(N)的时间复杂度;
● 请你说出几种基本的数据结构,
参考回答:
常见的基本的数据结构有链表、栈、队列、树(只列出面试常考的基本数据结构)
1、链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点组成,这些节点不必在内存中相连。每个节点由数据部分Data和链部分Next,Next指向下一个节点,这样当添加或者删除时,只需要改变相关节点的Next的指向,效率很高。
栈和队列是比较特殊的线性表
栈是限制插入和删除只能在一个位置上进行的表,后进先出
队列只允许在front端进行删除操作,在rear端进行插入操作,
树:树型结构是一类非常重要的非线性数据结构,考察主要以二叉树为主,
● 手写代码:怎么判断链表有环,怎么找环节点
参考回答:
判断是否有环以及环节点
public class Solution {
ListNode EntryNodeOfLoop(ListNode h){
if(h == null || h.next == null)
return null;
ListNode slow = h;
ListNode fast = h;
while(fast != null && fast.next != null ){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
ListNode p=h;
ListNode q=slow;//相当于让q指向了m1
while(p != q){
p = p.next;
q = q.next;
}
if(p == q)
return q;
}
}
return null;
}
● 手写代码:一个单向链表,给出头结点,找出倒数第N个结点,要求O(N)的时间复杂度;
参考回答:
JAVA版本:
public class Solution {
public ListNode FindNthToTail(ListNode head,int N) {
ListNode pre=null,p=null;
//两个指针都指向头结点
p=head;
pre=head;
//记录N值
int a=N;
//记录节点的个数
int count=0;
//p指针先跑,并且记录节点数,当p指针跑了N-1个节点后,pre指针开始跑,
//当p指针跑到最后时,pre所指指针就是倒数第N个节点
while(p!=null){
p=p.next;
count++;
if(N<1){
pre=pre.next;
}
N--;
}
//如果节点个数小于所求的倒数第N个节点,则返回空
if(count<a) return null;
return pre;
}
}
C/C++版本:
class Solution {
public:
ListNode* FindNthToTail(ListNode* pListHead, unsigned int N) {
if(pListHead==NULL||N==0)
return NULL;
ListNode*pTail=pListHead,*pHead=pListHead;
for(int i=1;i<N;++i)
{
if(pHead->next!=NULL)
pHead=pHead->next;
else
return NULL;
}
while(pHead->next!=NULL)
{
pHead=pHead->next;
pTail=pTail->next;
}
return pTail;
}
};
Python:
class Solution:
def FindNthToTail(self, head, N):
# write code here
res=[]
while head:
res.append(head)
head=head.next
if N>len(res) or N<1:
return
return res[-N]
● 请问如何判断一个单向链表存在回路?
参考回答:
方法1:用一个指针数组A,存储已访问过的节点。用一个指针p,每次在链表上移动一步,然后与指针数组A比较,若数组中没有指针与p相同,说明第一次访问p,将p放入数组中;若有指针与p相同,则存在环路,且第一次相同的节点就是环的入口点。
链表长度为n,则需要空间o(n),且每次要与指针数组比较,时间复杂度为 O(n^2)。
方法2:在节点上记录该节点是否被访问过,如果在指针移动过程中遇到已访问过的节点,说明存在环路。同样地,第一次相同的节点就是环的入口点。
方法3:用两个指针,pSlow,pFast,一个慢一个快,慢的一次跳一步,,快的一次跳两步,如果快的能追上慢的就表示有环(pSlow == pFast )。
● 请问如何判断一个链表是否有环
参考回答:
方法1:用一个指针数组A,存储已访问过的节点。用一个指针p,每次在链表上移动一步,然后与指针数组A比较,若数组中没有指针与p相同,说明第一次访问p,将p放入数组中;若有指针与p相同,则存在环路,且第一次相同的节点就是环的入口点。
链表长度为n,则需要空间o(n),且每次要与指针数组比较,时间复杂度为 O(n^2)。
方法2:在节点上记录该节点是否被访问过,如果在指针移动过程中遇到已访问过的节点,说明存在环路。同样地,第一次相同的节点就是环的入口点。
方法3:用两个指针,pSlow,pFast,一个慢一个快,慢的一次跳一步,,快的一次跳两步,如果快的能追上慢的就表示有环(pSlow == pFast )。
● 请问如何判断两个链表是否相交
参考回答:
从头遍历两个链表。创建两个栈,第一个栈存储第一个链表的节点,第二个栈存储第二个链表的节点。每遍历到一个节点时,就将该节点入栈。两个链表都入栈结束后。则通过top判断栈顶的节点是否相等即可判断两个单链表是否相交。因为我们知道,若两个链表相交,则从第一个相交节点开始,后面的节点都相交。 若两链表相交,则循环出栈,直到遇到两个出栈的节点不相同,则这个节点的后一个节点就是第一个相交的节点。
node temp=NULL; //存第一个相交节点
while(!stack1.empty()&&!stack1.empty()) //两栈不为空
{
temp=stack1.top();
stack1.pop();
stack2.pop();
if(stack1.top()!=stack2.top())
{
break;
}
}
● 手写代码:循环链表插入元素
参考回答:
typedef struct _tag_CircleListNode
{
struct _tag_CircleListNode * next;
}CircleListNode;
typedef struct _tag_CircleList
{
CircleListNode header;
CircleListNode* slider;
int length;
}TCircleList;
//插入元素
int CircleList_insert(CircleList* list, CireListNode* node, int pos)
{
int ret = 0, i=0;
TCircleList* sList = (TCircleList*)list;
if (list == NULL || node== NULL || pos<0)
{
return -1;
}
CircleListNode* current = (CircleListNode*)sList;
for(i=0; (i<pos) && (current->next != NULL); i++)
{
current = current->next;
}
//current->next 0号节点的地址
node->next = current->next; //1
current->next = node; //2
//若第一次插入节点
if( sList->length == 0 )
{
sList->slider = node;
}
sList->length++;
//若头插法 current仍然指向头部
//(原因是:跳0步,没有跳走) 中间第一种情况
if( current == (CircleListNode*)sList )
{
//获取最后一个元素
CircleListNode* last = CircleList_Get(sList, sList->length - 1);
last->next = current->next; //3
}
return ret;
}
CircleListNode* CircleList_Get(CircleList* list, int pos) // O(n)
{
TCircleList* sList = (TCircleList*)list;
CircleListNode* ret = NULL;
int i = 0;
if (list==NULL || pos<0)
{
return NULL;
}
{
CircleListNode* current = (CircleListNode*)sList;
for(i=0; i<pos; i++)
{
current = current->next;
}
ret = current->next;
}
return ret;
}