顺序表和链表的相关热点面试题

来源:互联网 发布:单页seo怎么赚钱 编辑:程序博客网 时间:2024/05/16 01:05

一,比较顺序表和链表的优缺点,说说他们分别在什么场景下使用?

  1.        顺序表支持随机访问,单链表不支持
  2.        顺序表插入和删除数据效率很低,时间复杂度为O(N)(除尾插尾删),单链表插入删除效率更高时间复杂度为O(1)
  3.        顺序表的CPU高速缓存效率更高,单链表CPU告诉缓存低

二,删除一个无头单链表的非尾节点


三,在一个无头单链表的一个非头节点前插入一个节点


四,单链表实现约瑟夫环

约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依规律重复下去,直到圆桌周围的人全部出列。


代码实现:

#include <iostream>
using namespace std;


struct Node
{
int data;
Node * next;
};


class Josephus
{
private:
Node * rear;
public:
Josephus(){
rear = new Node;
rear->next = NULL;
}


Josephus(int n)// 构造循环单链表
{
Node * s = new Node;
Node * p = s;
s->data = 1;
rear = s;
for (int i = 2; i <= n; i++)
{
s = new Node;
s->data = i;
rear->next = s;
rear = s;
}
rear->next = p;
}


void choose(int k)
{
rear = rear->next;//rear指向头
while (rear)
{
for (int i = 1; i<k; i++)
{
rear = rear->next;
}


Node * p = rear->next;
if (p)
{
cout << rear->data << " ";
rear->data = p->data;
rear->next = p->next;
if (p == rear)
rear = NULL;
delete p;
}
}
cout << endl;
}
};


int main()
{
Josephus jos(10);
jos.choose(3);
system("pause");
return 0;
}

图解:


运行结果:


五,查找链表的中间节点

要求:只遍历一次链表

思想:

    1)设置2个指针,一个走2步时,另一个走1步;

    2)那么一个走到头时,另一个走到中间。

代码实现:

Node *FindMid(list *head)//遍历一次查找中间节点
{
Node *p1 = *head;
Node *p2 = *head;


    //p2走两步,p1走一步,p2走完,p1就是中间节点
while (p2)
{
p2 = p2->next;
if (p2 != NULL)
{
p2 = p2->next;
p1 = p1->next;
}
}
return p1;
}

运行结果:


六,查找单链表的倒数第k个结点,要求只能遍历一遍链表

思想:

       比较传统的做法就是,既然要查找倒数k个结点,可以先编译一遍链表,看链表中有多少个结点n,然后按顺序查找第n-k+1个结点就是要找的结点。但是这种方法需要遍历链表2遍,并不是很高效。

      一种更高效的方法是:用两个指针,一个指针先走k-1步,然后第二个指针也开始跟着一起走。当第一个指针到链表尾部的时候,第二个指针指向的就是链表中的导数第k个结点。

如查找倒数第3个结点:


代码实现:

Node *FindreversedK(list *head,int k)
{
if (head == NULL)
return NULL;
if (k == 0)
{
Node *p1 = *head;
Node *p2 = NULL;
while (p1 != NULL)
{
p2 = p1;
p1 = p1->next;
}
return p2;
}
Node *p1 = *head;
Node *p2 = *head;
while (k - 1)
{
if (p1->next != NULL)
p1 = p1->next;
else//该链表中的节点少于k个的时候
return NULL;
k--;
}
while (p1->next != NULL)
{
p1 = p1->next;
p2 = p2->next;
}
return p2;
}

运行结果:


七,判断链表是否带环,若带环,求环的长度,和入口。

设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇(fast如果为NULL,则为无环链表)

代码实现:

bool IsExitLoop(list *head)//是否带环
{
Node *slow = *head;
Node *fast = *head;
while (fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}

if (fast == NULL || fast->next == NULL)
return false;
return true;
}

//找入口结点

代码实现:

//碰撞点p到连接点的距离=头指针到连接点的距离
Node *FindEntrance(list *head)
{
Node *slow = *head;
Node *fast = *head;
while (fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}
if (fast == NULL || fast->next == NULL)
return NULL;
slow = *head;
while (fast != slow)
{
slow = slow->next;
fast = fast->next;
}
return fast;
}

求环的长度

代码实现:

//记录下会面的点meet,slow、fast从该点开始,
//再次碰撞所走过的操作数就是环的长度d
int GetPathDistance(list *head)
{
Node *slow = *head;
Node *fast = *head;
while (fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}
if (fast == NULL || fast->next == NULL)
return 0;
Node *meet = fast;
slow = slow->next;
fast = fast->next->next;
int distance = 0;
while (slow != fast)
{
distance++;
slow = slow->next;
fast = fast->next->next;
}
return distance + 1;
}

求整个带环链表的总长度

总长度=环的长度+头节点到入口的长度

代码实现:

//求总长度
int GetAllPathLen(list *head)
{
int d1 = 0;//从头到入口的距离
int d2 = 0;//带环的距离
int d = 0;//总距离
Node*p1 = *head;
while (p1->next != (FindEntrance(head)))
{
d1++;
p1 = p1->next;
}
d2 = GetPathDistance(head);
d = d1 + d2;
return d;
}

主函数:

int main()
{
list mylist;
In_List(&mylist);
create_list(&mylist);
if (IsExitLoop(&mylist) == 1)
printf("有环\n");
else
printf("无环\n");
Node *p = FindEntrance(&mylist);
printf("环的入口为:%d\n", p->data);
int d = GetPathDistance(&mylist);
printf("环的长度为:%d\n", d);
int len = GetAllPathLen(&mylist);
printf("环的总长度为:%d\n", len);
//showlist(mylist);
//Node *p = FindMid(&mylist);
//Node *p = FindreversedK(&mylist, 3);
//printf("%d\n", p->data);


system("pause");
return 0;
}

运行结果:


0 0
原创粉丝点击