● 请你说出几种基本的数据结构,
参考回答:
常见的基本的数据结构有链表、栈、队列、树(只列出面试常考的基本数据结构)
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
复制代码1
2
3
4
5
6
7
8
9
10while(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
C/C++版本:
复制代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32class Solution {
public:
ListNode* FindNthToTail(ListNode* pListHead, unsignedint N) {
if(pListHead==NULL||N==0)
return NULL;
ListNode*pTail=pListHead,*pHead=pListHead;
for(int i=1;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()) //两栈不为空
复制代码1
2
3
4
5
6
7
8
9{
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; (inext != NULL); i++){current = current->next;}
//current->next 0号节点的地址
复制代码1
2node->next = current->next; //1
current->next = node; //2
//若第一次插入节点
if( sList->length == 0 ){sList->slider = node;}sList->length++;
//若头插法 current仍然指向头部
//(原因是:跳0步,没有跳走) 中间第一种情况
复制代码1
2if( current == (CircleListNode*)sList )
{
//获取最后一个元素
复制代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24CircleListNode* 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
{
current = current->next;
}
ret = current->next;
}
return ret;
}