判断一个单链表是否有环及环的链接点

合集下载

链表的经典习题

链表的经典习题

链表的经典习题练习1•:链式栈//链式栈(top指向第⼀位⽆效的头结点)public class LinkedStack<E> {Node<E> top;//内部类class Node<E> {protected E data;protected Node<E> next;public Node(E data) {this.data = data;}}public LinkedStack() {top = new Node(new Object());}//头插法public void push(E val){Node<E> newNode=new Node(val) ;//创建⼀个值为val的节点newNode.next=top.next; //新插⼊节点的next指向原top指向的nexttop.next=newNode;//再把top.next指向新节点}//获取栈顶元素并删除public E remove(){if(top.next==null){throw new UnsupportedOperationException("the stack has been empty");}E result=top.next.data;top.next=top.next.next;return result;}public E peek(){if(top.next==null){throw new UnsupportedOperationException("the stack has been empty");}return top.next.data;}//从top节点的下⼀个开始遍历,不为空则⼀直的打印public void show(){Node<E> tmp=top.next;while (tmp!=null){System.out.print(tmp.data+" ");tmp=tmp.next;}System.out.println();}public static void main(String[] args) {LinkedStack<Integer> l=new LinkedStack();l.push(3);l.push(4);l.push(5);l.show();System.out.println(l.peek());l.remove();//删除5l.show();l.remove();//删除4l.show();l.remove();//删除3l.show();}}练习2:查找链表中倒数第k个节点//单链表查找倒数第k个节点class FindLastK<E> {Node<E> head;class Node<E> {protected E data;protected Node<E> next;public Node(E data,Node<E> next) {this.data = data;this.next=next;}}////构造函数,第⼀个头结点有效,所以不需要构造函数// public FindLastK() {// this.head = new Node(new Object(),null);// }//找倒数第k个数的⽅法public E lastK(int k){Node<E> cur1=this.head.next;Node<E> cur2=this.head;//cur2指向头if(head==null){return null;}if(k>getLenth()||k<=0){return null;}else if(k==getLenth()){return head.data;}for(int i=1;i<=k;i++) {cur2=cur2.next;if(cur2==null){return null;}}while (cur2.next!=null){cur2=cur2.next;cur1=cur1.next;}return cur1.data;}//获取链表长度public int getLenth(){int length=0;Node<E> cu=head;if(head==null){return 0;}while (cu!=null){ //应该让cur去遍历,不能让head直接遍历,否则打印⼀次后show再次打印链表就会空 length++;cu=cu.next;}return length;}//尾插法public void add(E val) {Node<E> newNode = new Node(val,null);Node<E> current = head;if(head==null){head=newNode;return;}while (current.next != null) {current = current.next;}current.next = newNode;// newNode.next=null;}public void show() {Node<E> current = head;if(current==null){System.out.println("链表空!!");return;}while (current!=null&&current.next!= null) {System.out.print(current.data + " ");current = current.next;}System.out.println(current.data);}}public class FindLastKTest{public static void main(String[] args) {FindLastK<Integer> f=new FindLastK<>();f.add(3);f.add(4);f.add(5);f.add(6);f.show();System.out.println("该链表的长度:"+f.getLenth());System.out.println(stK(1));//6System.out.println(stK(4));//3System.out.println(stK(5));//nullf.show();}}练习3:找到带环链表的⼊⼝节点import sun.awt.image.ImageWatched;//单链表查找倒数第k个节点public class LinkedExercise<E> {Node<E> head;static class Node<E> {protected E data;public Node<E> next;public Node(E data, Node<E> next) {this.data = data;this.next = next;}}////构造函数,第⼀个头结点有效,所以不需要构造函数// public FindLastK() {// this.head = new Node(new Object(),null);// }//找倒数第k个数的⽅法public E lastK(int k) {Node<E> cur1 = this.head.next;Node<E> cur2 = this.head;//cur2指向头if (head == null) {return null;} else if (k > getLenth() || k <= 0) {return null;}//如果找的倒数第k个恰好为链表长度,直接将头结点的数返回else if (k == getLenth()) {return head.data;}for (int i = 1; i <= k; i++) {cur2 = cur2.next;if (cur2 == null) {return null;}}//两个节点同时遍历,快节点遍历到最后⼀个节点时,慢节点指向的节点就是要找的节点while (cur2.next != null) {cur2 = cur2.next;cur1 = cur1.next;}return cur1.data;}//获取链表长度public int getLenth() {int length = 0;Node<E> cu = head;if (head == null) {return 0;}while (cu != null) { //应该让cur去遍历,不能让head直接遍历,否则打印⼀次后show再次打印链表就会空 length++;cu = cu.next;}return length;}//判断单链表是否有环/*** 快慢指针,先通过两个指针找到环内的节点,然后再⼀个节点从相交节点出发,* 另⼀个节点从头结点出发,再次相交的节点就是环的⼊⼝节点** @return*/public E getLinkCirclrVal() {Node<E> slow = this.head;Node<E> fast = this.head;//找到了相交节点while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;if (slow == fast) {break;}}if (fast == null) {return null;} else {fast = this.head;while (fast != slow) {fast = fast.next;slow = slow.next;}return slow.data;}}//尾插法public void add(E val) {Node<E> newNode = new Node(val, null);Node<E> current = head;if (head == null) {head = newNode;return;}while (current.next != null) {current = current.next;}current.next = newNode;// newNode.next=null;}public void show() {Node<E> current = head;if (current == null) {System.out.println("链表空!!");return;}while (current != null && current.next != null) {System.out.print(current.data + " ");current = current.next;}System.out.println(current.data);}//构造带环的链表public void con(LinkedExercise<E> link){//将两个节点都指向头LinkedExercise.Node list=link.head;LinkedExercise.Node p=link.head;//list遍历到最后⼀个节点while (list.next!=null){list=list.next;}//让最后⼀个节点的写⼀个指向头结点的下⼀个 6指向5list.next=p.next.next;}public static void main(String[] args) {LinkedExercise<Integer> f = new LinkedExercise<>();f.add(3);f.add(4);f.add(5);f.add(6);f.show();System.out.println("该链表的长度:" + f.getLenth());System.out.println(stK(1));//6System.out.println(stK(4));//3System.out.println(stK(5));//nullf.con(f);System.out.println("环的⼊⼝节点:"+f.getLinkCirclrVal());}}练习4:合并两个有序的链表(头结点⽆效时)包含头结点⽆效的⼤多数函数:class SingleLinekdListTakeHead<E extends Comparable> {protected Node<E> head;//头节点class Node<E> {protected E data;//数据域protected Node<E> next;//next引⽤域public Node(E data, Node<E> next) {this.data = data;this.next = next;}}//初始化headpublic SingleLinekdListTakeHead() {head = new Node(new Object(), null);}//在head之后直接插⼊⼀个节点,头插法public void addHead(E element) {Node<E> newNode = new Node(element, null);newNode.next = head.next;//先让新添加的节点的下⼀个指向原head节点指向的 head.next = newNode;//再让head节点指向新节点}//尾插法public void addTail(E element) {Node<E> newNode = new Node(element, null);Node<E> tail = head;//定义⼀个节点从头⾛到尾//tail⾛到当前链表的尾部while (tail.next != null) {tail = tail.next;}tail.next = newNode;newNode.next=null;}/*** 固定位置插⼊⼀个节点* 判断参数合法性* 找到pos位置的前⼀个节点* @param pos 固定位置* @param element 元素*/public void addPos(int pos, E element) {if (pos <= 0 || pos > getLength()) {return;}Node<E> prev = head.next;int index = 1;while (index++ < pos - 1) {prev = prev.next;}Node<E> newNode = new Node<>(element, null);newNode.next = prev.next;prev.next = newNode;}//删除元素为element的节点public boolean remove(E element) {//如果只有⼀个头结点,返回falseif (head.next == null) {return false;}//找到该元素所对应的节点 + 该元素所对应的节点的前⼀个 //从头结点开始遍历Node<E> tmp = head;while (tmp != null) {if (tmp.next != null && tmp.next.data == element) {//tmp.next是我们要删除的节点 tmp是删除节点的前⼀个 tmp.next = tmp.next.next;return true;}tmp = tmp.next;}return false;}//设置某个位置的值为newElementpublic void set(int pos, E newElement){if(pos <= 0 || pos > getLength()){return;}//找pos位置的节点Node<E> tmp = head.next;for(int i=1; i < pos; i++){tmp = tmp.next;}tmp.data = newElement;}//得到某个元素的值public E get(E element){Node<E> tmp = head.next;//从有效节点开始遍历while(tmp != null){if(tmp.data == element){return tmp.data; //找到的话,返回该节点}tmp = tmp.next;}return null;} //合并两个有序的单链表public void merge(SingleLinekdListTakeHead<E> list2){// LinkedExercise<E> list3=new LinkedExercise<>();Node<E> p=this.head;//最后合并成功的的链表Node<E> p1=this.head.next;//第⼀的链表Node<E> p2=list2.head.next;//第⼆个链表while (p1!=null && p2!=null){if(pareTo(p2.data)>=0){p.next=p2;//list3.add(p2.data);p2=p2.next;}else {p.next=p1;// list3.add(p1.data);p1=p1.next;}p=p.next;}if(p1!=null){ //链表1还有剩余节点p.next=p1;}p.next=p2;}// return p.data;}//返回长度public int getLength() {Node<E> tmp = head.next;int length = 0;while (tmp != null) {length++;tmp = tmp.next;}return length;}//打印栈public String toString() {StringBuilder strs = new StringBuilder();Node<E> tmp = head.next;while (tmp != null) {strs.append(tmp.data + " ");tmp = tmp.next;}return strs.toString(); //strs是StringBuilder类型,应该添加toString⽅法,才能返回String类型的 }//逆置带有头结点的单链表public void reverse(){if(head.next==null||head.next.next==null){return;}else {Node<E> cur=this.head.next.next;//指向第⼆个有效的节点this.head.next.next=null;Node<E> pos=null;while (cur!=null){pos=cur.next;//先将cur.next指向poscur.next=head.next;head.next=cur;//头插法,将节点插在head后cur=pos;}}}}public class Linked {public static void main(String[] args) {SingleLinekdListTakeHead<Integer> list=new SingleLinekdListTakeHead();list.addHead(3);list.addHead(5);list.addHead(8);System.out.println(list.toString());//8 5 3list.addTail(1);list.addTail(2);list.addTail(4);System.out.println(list.toString());//8 5 3 1 2 4list.reverse();System.out.println(list.toString());// list.addPos(2, 100); //在2 号位置加⼊元素100// System.out.println(list.toString());// list.addPos(0, 1000);// System.out.println(list.toString());//// list.remove(4);// System.out.println("删除值为4的元素:"+list.toString());//// list.set(2,2);//true,把2号元素的值改为2// System.out.println("把2号元素的值改为2:"+list.toString());// System.out.println(list.get(3));SingleLinekdListTakeHead list1=new SingleLinekdListTakeHead();list1.addTail(2);list1.addTail(6);list1.addTail(7);SingleLinekdListTakeHead list2=new SingleLinekdListTakeHead();list2.addTail(3);list2.addTail(4);list2.addTail(5);list2.addTail(9);list2.addTail(10);list1.merge(list2);System.out.println(list1.toString());}}练习5:链式队列package Exercise;public class LinkedQueue<T> {private Entry<T> front;private Entry<T> rear;private int count;public LinkedQueue(){this.front=this.rear=new Entry<>(null,null);}class Entry<T>{T data;Entry<T> next;public Entry(T data,Entry<T> next){this.data=data;this.next=next;}}public void offer(T data){Entry<T> node=new Entry<>(data,null);this.rear.next=node;this.rear=node;this.count++;}/***出队列需要判断队列空的情况,头节点⽆效;如果队列为空,需要将front和rear都指向空*/public void poll(){if(this.front.next!=null){this.front.next=this.front.next.next;if(this.front.next == null){this.rear = this.front;}this.count--;}}public int size(){return this.count;}public T peek(){return this.front.next.data;}public void show(){Entry<T> cur=this.front.next;while (cur!=null){System.out.print(cur.data+" ");cur=cur.next;}System.out.println();}public static void main(String[] args) {LinkedQueue l=new LinkedQueue();for (int i = 0; i < 4; i++) {l.offer(i);}l.show();System.out.println("队头元素为:"+l.peek());System.out.println("队列长度为:"+l.size());l.poll();l.show();}}难点:内部类和外部类的构造函数都需要对相应属性做初始化。

链表中有环的判定方法

链表中有环的判定方法

链表中有环的判定方法
链表是一种常见的数据结构,由一系列节点组成,每个节点都包含一个指向下
一个节点的指针。

在某些情况下,链表可能存在环,即其中一个节点的指针指向了之前出现过的节点,形成一个闭合的环路。

判定链表中是否存在环是一个常见的问题,下面将介绍两种常用的方法。

1. 快慢指针法:
这是一种经典的方法,通过设置两个指针,一个慢指针每次移动一步,一个
快指针每次移动两步。

如果链表中存在环,那么快指针最终将会追上慢指针,形成一个循环。

如果快指针在某个节点遇到了空指针,则说明链表中没有环。

这个方法的时间复杂度是O(n),其中n是链表的长度。

2. 哈希表法:
这种方法借助哈希表的特性来解决问题。

遍历链表中的每个节点,将每个节
点存储到哈希表中。

在存储之前,先检查该节点是否已经存在于哈希表中,如果存在,则说明链表中存在环。

这个方法的时间复杂度为O(n),其中n是链表的长度。

但是空间复杂度为O(n),因为需要存储每个节点的引用信息。

以上是链表中有环的判定方法。

快慢指针法是常用且高效的解决方案,可以在
实践中广泛应用。

而哈希表法则提供了另一种思路,但在空间复杂度上相对较高。

根据实际情况选择合适的方法,既能满足需求,又能提高代码的效率。

hashmap解决环形链表的方式

hashmap解决环形链表的方式

一、hashmap简介hashmap是一种常用的数据结构,在解决环形链表问题时有着重要的作用。

hashmap是一种以键值对存储数据的数据结构,可以通过键快速定位到对应的值,这使得它在解决环形链表问题时能够高效地处理数据。

二、环形链表问题的描述环形链表是指链表中的最后一个节点指向链表中的一个前面的节点, 而不是NULL。

解决环形链表问题的一种常见方法是使用快慢指针。

快慢指针分别以不同的速度遍历链表,如果链表中存在环形结构,则快指针迟早会追上慢指针。

另一种解决环形链表问题的方法是使用hashmap。

三、使用hashmap解决环形链表问题的方式在使用hashmap解决环形链表问题时,可以按照以下步骤进行操作:1. 创建一个hashmap,用于存储链表中的节点。

2. 遍历链表,将每个节点依次存入hashmap中。

在存入节点之前,需先判断该节点是否已经在hashmap中出现过,如果出现过,则说明链表中存在环形结构。

3. 如果遍历完成后没有发现重复节点,则链表中不存在环形结构。

四、hashmap解决环形链表问题的代码示例以下是使用hashmap解决环形链表问题的代码示例(Java语言):```public class Solution {public boolean hasCycle(ListNode head) {Map<ListNode, Integer> map = new HashMap<>();ListNode curr = head;while (curr != null) {if (map.cont本人nsKey(curr)) {return true;} else {map.put(curr, 1);curr = curr.next;}}return false;}}```在上述代码中,我们首先创建了一个HashMap对象map。

然后我们使用while循环遍历链表,对每个节点进行判断。

数据结构面试题(含答案)

数据结构面试题(含答案)

1.栈和队列的共同特点是(只允许在端点处插入和删除元素)4.栈通常采用的两种存储结构是(线性存储结构和链表存储结构)5.下列关于栈的叙述正确的是(D)A.栈是非线性结构B.栈是一种树状结构C.栈具有先进先出的特征D.栈有后进先出的特征6.链表不具有的特点是(B)A.不必事先估计存储空间 B.可随机访问任一元素C.插入删除不需要移动元素D.所需空间与线性表长度成正比7.用链表表示线性表的优点是(便于插入和删除操作)8.在单链表中,增加头结点的目的是(方便运算的实现)9.循环链表的主要优点是(从表中任一结点出发都能访问到整个链表)10.线性表L=(a1,a2,a3,……ai,……an),下列说法正确的是(D)A.每个元素都有一个直接前件和直接后件B.线性表中至少要有一个元素C.表中诸元素的排列顺序必须是由小到大或由大到小D.除第一个和最后一个元素外,其余每个元素都有一个且只有一个直接前件和直接后件11.线性表若采用链式存储结构时,要求内存中可用存储单元的地址(D)A.必须是连续的B.部分地址必须是连续的C.一定是不连续的D.连续不连续都可以12.线性表的顺序存储结构和线性表的链式存储结构分别是(随机存取的存储结构、顺序存取的存储结构)13.树是结点的集合,它的根结点数目是(有且只有1)14.在深度为5的满二叉树中,叶子结点的个数为(31)15.具有3个结点的二叉树有(5种形态)16.设一棵二叉树中有3个叶子结点,有8个度为1的结点,则该二叉树中总的结点数为(13)17.已知二叉树后序遍历序列是dabec,中序遍历序列是debac,它的前序遍历序列是(cedba)18.已知一棵二叉树前序遍历和中序遍历分别为ABDEGCFH和DBGEACHF,则该二叉树的后序遍历为(DGEBHFCA)19.若某二叉树的前序遍历访问顺序是abdgcefh,中序遍历访问顺序是dgbaechf,则其后序遍历的结点访问顺序是(gdbehfca)20.数据库保护分为:安全性控制、完整性控制、并发性控制和数据的恢复。

东软集团面试题及答案

东软集团面试题及答案

东软集团面试题及答案1 堆和栈那个是对程序员透明的?答案:栈。

2 请结合具体实例阐述一下面向对象中"多态"的概念。

答案:同一操作用于不同的类的实例,不同的类将进行不同的解释,最后产生不同的结果,它有两种:→编译时多态---- 通过overload来实现,系统在编译时,根据传递的参数和返回的类型等信息决定实现何种操作→运行时多态---- 通过override来实现,根据运行时具体对象的类型决定调用哪个方法。

给我面试的人很有亲和力,我没有感到有压力。

首先是自我介绍;他根据你自己介绍的情况来问你问题,我主要是说得现在正在做的青软实训内部管理系统。

你在项目组里主要负责什么?是怎么做的?主要负责页面前台展示,我现在设计页面框架,用的Struts-tile;把页面分为4部分,sidebar,header,footer,context;然后把sidebar,header,footer 这些每个页面都要用的写成一个整体,放在配置文件中,每个页面都引用他,这样就不用变了。

变得就是context部分。

另外在sidebar里面,用javascript来实现导航栏的弹出功能。

用什么方法可以检测一个环行链表?首先可以在data里面初始化一个数据,然后向下循环,可以找到这条数据的话,就是环行链表,否则就不是;但这个链表不一定是完全的链表,里面可能有分支,该怎么检测?设置一个循环,条件设为true,如果可以结束就不是,不能结束里面就有环。

那这样的话怎么让他跳出循环呢?我也不知道,那请教一下吧。

你看看如果next引用如果相同的话是不是就是环行链表?对,是那你想个办法来实现他吧。

把这个next引用,写进一个list,以后都把next引用和list里面的内容进行比较,如果有相同的就是环,没有就不是。

但如果链表很大,而你做的是嵌入式项目,有内存溢出,该怎么检测呢?呵呵,我想不出来了。

1.2.自我介绍,(注自我介绍中的自我经历可能会成为他的考点)3.我在自我介绍中说了我参加过数学建模,他问了我我们建的什么模型,我给他介绍了一下。

C++常见笔试题及答案

C++常见笔试题及答案

C++面试题1 #include “filename.h”和#include <filename.h〉的区别?答:对于#include <filename.h>编译器从标准库开始搜索filename。

h对于#include “filename。

h"编译器从用户工作路径开始搜索filename.h2 头文件的作用是什么?答:一、通过头文件来调用库功能。

在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可.用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。

编译器会从库中提取相应的代码.二、头文件能加强类型安全检查。

如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则,能大大减轻程序员调试、改错的负担。

3 C++函数中值的传递方式有哪几种?答:C++函数的三种传递方式为:值传递、指针传递和引用传递。

4 内存的分配方式有几种?答:一、从静态存储区域分配。

内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量。

二、在栈上创建.在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。

栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

三、从堆上分配,亦称动态内存分配.程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。

动态内存的生存期由我们决定,使用非常灵活,但问题也最多.5 实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数;答:双向链表删除一个节点Ptemplate〈class type> void list〈type〉::delnode(int p){int k=1;listnode〈type〉*ptr,*t;ptr=first;while(ptr->next!=NULL&&k!=p){ptr=ptr—>next;k++;}t=ptr-〉next;cout〈〈”你已经将数据项”〈<t-〉data〈〈"删除"〈〈endl;ptr—〉next=ptr-〉next—>next;length-—;delete t;}在节点P后插入一个节点:template〈class type> bool list〈type〉::insert(type t,int p){listnode〈type〉*ptr;ptr=first;int k=1;while(ptr!=NULL&&k<p){ptr=ptr->next;k++;}if(ptr==NULL&&k!=p)return false;else{listnode<type〉*tp;tp=new listnode<type〉;tp->data=t;tp->next=ptr—〉next;ptr->next=tp;length++;return true;}}6 写一个函数,将其中的\t都转换成4个空格。

常见算法面试题及答案

常见算法面试题及答案

常见算法面试题及答案算法面试是程序员求职过程中的重要环节之一。

面试官会通过提问算法问题来评估面试者的思维能力、解决问题的能力以及编程技巧。

在准备算法面试的过程中,了解常见的算法面试题并熟悉相应的解答方法是非常重要的。

本篇文章将介绍一些常见的算法面试题及其答案,帮助读者更好地准备算法面试。

1. 两数之和题目描述:给定一个整数数组和一个目标值,判断数组中是否存在两个数之和等于目标值。

若存在,返回这两个数的索引,若不存在,返回空。

解答方法:使用哈希表来记录数组元素和索引的对应关系。

遍历数组,对于每个元素,判断目标值与当前元素的差值是否在哈希表中存在,如果存在则返回对应的索引;如果不存在,则将当前元素及其索引插入哈希表中。

当遍历结束后仍未找到满足条件的两个数,返回空。

2. 反转字符串题目描述:给定一个字符串,将其按照单词顺序进行反转。

解答方法:首先,将整个字符串进行反转,得到一个逆序的字符串。

然后,再将逆序字符串中的每个单词进行反转。

最后得到的字符串即为所求结果。

3. 链表是否存在环题目描述:给定一个链表,判断链表中是否存在环。

解答方法:使用快慢指针的方法来判断链表是否存在环。

快指针每次移动两步,慢指针每次移动一步。

如果链表中存在环,那么快指针和慢指针一定会在某个节点相遇;如果链表中不存在环,快指针将会先到达链表的末尾。

根据快慢指针的移动情况,可以判断链表是否存在环。

4. 二叉树的最大深度题目描述:给定一个二叉树,找出其最大深度。

解答方法:使用递归的方法来计算二叉树的最大深度。

从根节点开始,递归地计算左子树和右子树的最大深度,然后取二者中的较大值加上1即为整个二叉树的最大深度。

5. 最长连续递增序列题目描述:给定一个未经排序的整数数组,找到最长连续递增序列的长度。

解答方法:遍历数组,对于每个元素,若与前一个元素递增,则将连续递增序列长度加1,否则重新计算连续递增序列的长度。

在遍历过程中,记录最长的连续递增序列长度,并返回结果。

单链表解决问题的方法总结

单链表解决问题的方法总结

单链表解决问题的方法总结单链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的引用。

单链表可以解决各种问题,以下是一些常见的方法总结。

1. 插入节点:单链表的插入操作可以在任意位置插入节点。

可以在链表的头部或尾部插入节点,也可以在指定位置插入节点。

插入节点的过程包括创建新节点、修改前后节点的引用指针。

通过合适的引用指针操作,可以高效地插入节点。

2. 删除节点:单链表的删除操作可以删除任意位置的节点。

删除节点的过程包括修改前后节点的引用指针,使它们直接指向彼此。

通过合适的引用指针操作,可以高效地删除节点。

需要注意的是,在删除节点前,需要判断节点是否存在,避免出现空指针异常。

3. 查找节点:单链表的查找操作可以查找指定数值或者位置的节点。

从链表的头部开始遍历,依次比较节点的数值或位置,直到找到目标节点。

查找节点的过程需要遍历整个链表,时间复杂度为O(n)。

可以通过合适的算法优化来提高查找效率。

4. 反转链表:单链表的反转操作可以将链表中的节点顺序颠倒。

可以使用三个指针来完成反转,分别指向当前节点、前一个节点和后一个节点。

通过依次修改指针的指向,可以实现链表的反转。

5. 链表合并:单链表的合并操作可以将两个有序链表合并为一个有序链表。

可以比较两个链表的节点数值大小,按照顺序连接节点,直到其中一个链表为空。

最后将剩余的节点连接到新链表的末尾。

6. 环检测:单链表中的环检测是判断链表中是否存在循环的操作。

通过使用两个指针,一个快指针和一个慢指针,从链表的头部开始向前移动。

如果存在循环,则快指针和慢指针会在某个节点相遇。

可以通过这个特性来判断链表中是否存在循环。

这些是单链表解决问题的一些常见方法总结。

根据具体的问题需求,选择合适的方法可以高效地操作单链表,实现所需功能。

如何检测一个较大的单链表是否有环

如何检测一个较大的单链表是否有环

如何检测一个较大的单链表是否有环单链表有环指的是单链表中某个结点的next指针域指向的是链表中在它之前的某一个结点,这样在链表的尾部形成一个环形结构。

检测单链表是否有环,一般有以下几种方法。

方法一,定义一个指针数组,初始化为空指针,从链表的头指针开始往后遍历,每次遇到一个指针就跟指针数组中的指针相比较,若没有找到相同的指针,说明这个结点是第一次访问,还没有形成环,将这个指针添加到指针数组中去。

若在指针数组中找到了同样的指针,说明这个结点已经被访问过了,于是就形成了环。

方法二,定义两个指针fast与slow,二者的初始值都指向头,slow每次前进一步,fast每次前进两步,两个指针同时向前移动,快指针每移动一次都要跟慢指针比较,直到当快指针等于慢指针为止,就证明这个链表是带环的单向链表,否则,证明这个链表是不带环的循环链表(fast先行到达尾部为NULL,则为无环链表)。

struct listtype{int data;struct listtype *next;};typedef struct listtype *list;int IsLoop(list sll){list fast=sll;list slow=sll;if(fast==NULL){return -1;}while(fast&&fast->next){fast=fast->next->next;slow=slow->next;if(fast==slow){return 1;}map<node*,int>m;bool IsLoop(node *head){if(!head)return false;node *p=head;while(p){if(m[p]==0) // 默认值都是0m[p]=1;else if(m[p]==1)return true;p=p->next;}}如果单链表有环,按照方法二的思路,走得快的指针fast若与走的慢的指针slow 相遇时,slow指针肯定没有走遍历完链表,而fast指针已经在环内循环了n圈(1<=n)。

数据结构选择判断复习题

数据结构选择判断复习题

数据结构期中期末选择判断复习题判断题:U1-U31.(×)数据元素是数据的最小单位。

2.(√)健壮的算法不会因非法的输入数据而出现莫名其妙的状态。

3.(×)数据的逻辑结构是指数据的各数据项之间的逻辑关系。

4.(×)数据的逻辑结构说明数据元素之间的顺序关系,它依赖于计算机的存储结构。

5.(×)数据的物理结构是指数据在计算机内的实际存储形式。

6.(×)数据结构的抽象操作的定义与具体实现有关。

7.(×)顺序存储方式的优点是存储密度大,且插入,删除运算效率高。

8.(√)顺序存储方式插入和删除时的效率太低,在这方面它不如链式存储方式好。

9.(√)顺序存储结构的主要缺点是不利于插入和删除操作。

10.(×)对任何数据结构链式存储结构一定优于顺序存储结构。

11.(×)取线性表的第i个元素的时间同i的大小有关。

12.(√)线性表、栈和队列都是线性结构。

13.(√)链表是采用链式存储结构的线性表,进行插入、删除操作时,在链表中比在顺序存储结构中效率高。

14.(×)线性表中每一个元素均存在唯一一个前驱和唯一一个后继。

15.(×)循环链表不是线性表。

16.(×)线性表的长度是线性表所占用的存储空间的大小。

17.(×)在单链表表示的线性表中,取线性表的第i个元素操作的时间复杂度为O(1)。

18.(√)删除带头结点单链表的第一个元素结点的时间复杂度是O(1)。

19.(√)栈是实现过程和函数等子程序所必需的结构。

20.(√)栈是一种插入与删除操作都限定在表的一端进行的线性表。

21.(√)若输入序列为1,2,3,4,5,6,则通过一个栈可以输出序列3,2,5,6,4,1。

22.(×)在顺序存储结构表示的栈中删除一个元素时可能会引起栈内数据元素的移动。

23.(√)栈既可以采用顺序存储结构表示也可以采用链式存储结构表示。

C_c++语言面试宝典(保证你通过面试)

C_c++语言面试宝典(保证你通过面试)

12.将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 } 好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局 部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生 runtime error! 注意事项: (1)不能返回局部变量的引用。这条可以参照 Effective C++[1]的 Item 31。主要原因是 局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指 "的引用,程序会进入
2.delete 与 delete []区别
delete 只会调用一次析构函数,而 delete[] 会调用每一个成员的析构函数。在 More Effective C++中有更为详细的解释:“当 delete 操作符用于数组时,它为每个数组元素调用 析构函数,然后调用 operatordelete 来释放内存。”delete 与 New 配套,delete []与 new []配套
你真诚的朋友:左老师
1.new、delete、malloc、free 关系
delete 会调用对象的析构函数,和 new 对应 free 只会释放内存,new 调用构造函数。malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符。它们都可用于申请动态内 存和释放内存。对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。 对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于 malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析 构函数的任务强加于 malloc/free。因此 C++语言需要一个能完成动态内存分配和初始化工作 的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。注意 new/delete 不是 库函数。

链表常用方法

链表常用方法

链表常用方法
1.增加节点:链表的特性就是可以动态增加节点,常用的方式是在链表末尾添加新节点或在指定节点后添加新节点。

2. 删除节点:删除节点时需要注意保持链表的连续性,一般有
两种方式,一种是将该节点的前一个节点直接指向该节点的下一个节点,另一种是将该节点的值设为 null 或者其他特殊值,将该节点标记为删除。

3. 遍历链表:使用循环语句对链表进行遍历,依次访问每个节
点即可。

4. 查找节点:查找链表中的某个节点时可以使用循环遍历或者
递归查找的方式,如果链表是有序的,则可以使用二分查找的方式。

5. 反转链表:将链表中节点的指针反转即可实现链表的反转,
可以使用迭代或者递归的方式实现。

6. 合并链表:将两个有序链表合并成一个有序链表,可以使用
迭代或者递归的方式实现。

7. 判断链表是否存在环:使用两个指针分别从链表头开始遍历,一个指针每次移动一个节点,另一个指针每次移动两个节点,如果存在环,则两个指针一定会相遇。

8. 找到环的起点:使用上一步中相遇的节点作为起点,再使用
两个指针分别从该节点和链表头开始遍历,相遇的节点即为环的起点。

9. 删除倒数第 n 个节点:使用快慢指针的方式找到倒数第 n
个节点,然后删除该节点即可。

10. 检测链表是否回文:使用快慢指针将链表分成两部分,将后半部分反转,然后比较两部分是否相等。

floyd判圈法

floyd判圈法

Floyd判圈法一、引言在计算机科学中,Floyd判圈法(Floyd’s cycle-finding algorithm)是一种用于判断有向图中是否存在环的算法。

该算法由罗伯特·弗洛伊德(Robert W. Floyd)于1967年提出,因此得名。

Floyd判圈法通过使用两个指针在图中移动来判断是否存在环,并且可以找到环的起点。

二、算法原理Floyd判圈法的原理非常简单,主要分为以下几个步骤:1.定义两个指针,一个快指针(每次移动两步)、一个慢指针(每次移动一步)。

2.快指针从起点开始移动,慢指针从起点的下一个节点开始移动。

3.如果存在环,快指针最终会追上慢指针,两个指针会相遇。

4.如果不存在环,快指针会提前到达终点。

三、算法步骤下面详细介绍Floyd判圈法的具体步骤:1. 初始化指针设定两个指针,一个指向图中的起点,另一个指向起点的下一个节点。

slow_pointer = head.nextfast_pointer = head.next.next2. 移动指针通过不断移动指针来判断是否存在环。

while slow_pointer != fast_pointer:slow_pointer = slow_pointer.nextfast_pointer = fast_pointer.next.next3. 判断是否存在环当两个指针相遇时,即存在环,否则不存在环。

if slow_pointer == fast_pointer:return Trueelse:return False4. 寻找环的起点如果存在环,需要找到环的起点。

此时,将慢指针重新指向起点,快指针保持在相遇点,然后两个指针同时每次移动一步,直到相遇。

slow_pointer = headwhile slow_pointer != fast_pointer:slow_pointer = slow_pointer.nextfast_pointer = fast_pointer.nextreturn slow_pointer四、算法分析Floyd判圈法的时间复杂度为O(n),其中n表示链表的节点数。

计算机专业基础综合数据结构图历年真题试卷汇编7_真题无答案

计算机专业基础综合数据结构图历年真题试卷汇编7_真题无答案

计算机专业基础综合数据结构(图)历年真题试卷汇编7(总分62, 做题时间90分钟)7. 设计题1.已知连通图如下:(1)若从顶点B出发对该图进行遍历,在(1)的基础上分别给出本图的按深度优先搜索和按广度优先搜索的顶点序列;(2)写出按深度优先搜索的递归程序。

【厦门大学200l三(12%分)】SSS_TEXT_QUSTI2.设计算法以实现对无向图G的深度遍历,要求:将每一个连通分量中的顶点以一个表的形,式输出。

例如,下图的输出结果为:(1,3)(2,6,7,4,5,8)(9,10)。

注:本算法中可以调用以下几个函数:firstadj(g,1,)——返回图g中顶点v的第一个邻接点的号码,若不存在,则返回0。

nextadj(g,v,w)——返回图g中顶点v的邻接点中处于w之后的邻接点的号码,若不存在,则返回0。

nodes(g)——返回图g中的顶点数。

【合肥工业大学2000五、4(8分)】SSS_TEXT_QUSTI3.请设计一个图的抽象数据类型(只需要用类Pascal或类C/C++语言给出其主要功能函数或过程的接口说明,不需要指定存储结构,也不需要写出函数或过程的实现方法),利用抽象数据类型所提供的函数或过程编写图的广度优先周游算法。

算法不应该涉及具体的存储结构,也不允许不通过函数或过程而直接引用图结构的数据成员,抽象数据类型和算法都应该加足够的注释。

【北京大学1999二、1(10分)】SSS_TEXT_QUSTI4.设计算法以判断给定的无向图G中是否存在一条以网为起点的包含所有顶点的简单路径,若存在,返回TRUE,否则,返回FALSE(注:本算法中可以调用以下几个函数:FIRSTADJ(G,V)——返回图G中顶点V的第一个邻接点的号码,若不存在,则返回0;NEXTADJ(G,W)——返回图G中顶点V的邻接点中处于W之后的邻接点的号码,若不存在,则返回0;NODES(G)——返回图G中的顶点数)。

【合肥工业大学1999五、5(8分)】SSS_TEXT_QUSTI5.已有邻接表表示的有向图,请编程判断从第u顶点至第v顶点是否有简单路径,若有,则印出该路径上的顶点。

loop-detect用法

loop-detect用法

loop-detect用法loop_detect是undirected graph的方法,用于检测一个图中是否存在环路。

使用方法如下:1. 构建一个无向图。

可以使用邻接表或邻接矩阵等数据结构表示图。

2. 调用loop_detect方法,并传入图数据结构作为参数。

3. loop_detect方法将返回一个boolean值,表示图中是否存在环路。

示例代码如下:```pythondef loop_detect(graph):visited = set()for node in range(len(graph)):if node not in visited:if dfs(graph, node, visited, -1):return Truereturn Falsedef dfs(graph, node, visited, parent):visited.add(node)for neighbor in graph[node]:if neighbor not in visited:if dfs(graph, neighbor, visited, node):return Trueelif parent != neighbor:return Truereturn False# 构建无向图的示例graph = [[1, 2], [0, 2], [0, 1, 3], [2]]has_loop = loop_detect(graph)print(has_loop) # 输出:True,图中存在环路```在示例代码中,loop_detect方法利用深度优先搜索(DFS)算法,从每个未访问过的节点开始遍历图。

每次遍历时,使用visited和parent记录已访问过的节点和当前节点的父节点。

如果遇到一个已经访问过的节点,并且不是当前节点的父节点,说明图中存在环路,返回True。

如果遍历完所有节点都没有发现环路,则返回False。

计算机专业基础综合模拟试卷110_真题-无答案

计算机专业基础综合模拟试卷110_真题-无答案

计算机专业(基础综合)模拟试卷110(总分120,考试时间90分钟)1. 单项选择题单项选择题1-40小题。

下列每题给出的四个选项中,只有一个选项是最符合题目要求的。

1. 假设栈的容量为3,入栈的序列为1,2,3,4,5,则出栈的序列可能为( )。

A. 3,2,1,5,4B. 1,5,4,3,2C. 5,4,3,2,1D. 4,3,2,1,52. 当字符序列t3作为栈的输入时,则输出长度为3、且可用作C语言标识符的序列有( )个。

A. 4B. 5C. 3D. 63. 在下列遍历算法中,在遍历序列中叶结点之间的次序可能与其他算法不同的算法是( )。

A. 先序遍历算法B. 中序遍历算法C. 后序遍历算法D. 层次遍历算法4. 有关二叉树下列说法正确的是( )。

A. 二叉树的度为2B. 一棵二叉树的度可以小于2C. 二叉树中至少有一个结点的度为2D. 二叉树就是度为2的有序树5. 利用逐点插入建立序列(50,72,43,85,75,20,35,45,65,30)对应的二叉排序树后,要查找元素30要进行的元素间的比较次数是( )。

A. 4B. 5C. 6D. 76. 由4棵树组成的森林中,第一、第二、第三和第四棵树中的结点数分别为30、10、20、5,当把森林转换成二叉树后,对应二叉树中根结点的右子树的左子树的结点数为( )。

A. 29B. 9C. 25D. 197. 无向图G有23条边,度为4的顶点有5个,度为3的顶点有4个,其余都是度为2的顶点,则图G最多有( )个顶点。

A. 1 1B. 12C. 15D. 168. 假设有n个顶点e条边的有向图用邻接表表示,则删除与某个顶点v相关的所有边的时间复杂度为( )。

A. 0(n)B. 0(e)C. 0(n+e)D. 0(ne)9. 折半查找有序表(2,10,25,35,40,65,70,75,81,82,88,100),若查找元素75,需依次与表中元素( )进行比较。

如何判断链表中存在环路

如何判断链表中存在环路

如何判断链表中存在环路如果你曾经想过要参加⾯试,像我⼀样,你⼀定看过这个问题:如何判断链表中存在环路。

(我不太清楚这个问题的应⽤在哪⾥,烦请各位读者能够提⽰⼀下。

)先简单说⼀下我之前看到的⽅法。

⽅法⼀:蛮⼒法。

⽅法⼆:在链表中增加⼀个域visited,初始化都为0,从链表的头部开始⾛,每⾛过⼀个链表就标记visited为1,如果要访问的下⼀个节点的visited域为1,那么证明链表中有环。

⽅法三:如果不能增加域,可以设置⼀个数组,将已经访问的链表节点依次放⼊到数组中,在访问下⼀个节点时,如果这个节点的地址已经在数组中,那么也能证明链表中有环。

进⼊正题之前,先简单说⼀下今天的⾯试。

今天下午我参加⾯试的时候也被问到了这个问题,⾯试官是个南开的MM。

似乎她也意识到诸如此类的问题已经在⽹上司空见惯,所以就试探性的问我是不是之前已经在⽹上看到了⽅法。

我很⽼实,回答:嗯。

我俩相视⽽笑。

然后,她⼜问我⼀个问题:如何找到⼀个链表的中间元素?我想了想,灵光⼀闪,说:设置两个指针p和q,p⼀次⾛⼀下,q⼀次⾛两下,这样当q⾛到链表的尾部的时候,p应该正好⾛到链表的中间(我当时很佩服我⾃⼰。

)。

那个MM笑了⼀下,表⽰赞同,接下来随⼝的⼀句话把我带⼊了漩涡:嗯,刚才那个判断链表是否有环的问题也可以⽤这个⽅法。

顿时,我脸上呈现出迷茫的表情,好奇⼼驱使着我问道:真的?MM⼀看我竟然不知道这个⽅法,说:哦,原来你不知道啊!正好,你想想⽤你刚才说的⽅法解决⼀下链表有环的问题。

同志们,好奇⼼害死猫啊!然后我就囧了,折腾了半天,画了个链表,在上⾯试着画了⼀下,发现如果链表有环,有可能q会追上p,相当与长跑中的套圈。

但是,有⼀个问题:p是不是⼀定会和q重合?这个问题看起来有些复杂,我们现在来抽象⼀下,这个问题不是很难。

假设此时p点刚刚进⼊到链表中的环中,q在环中的某⼀个位置,如图所⽰。

设t时间后,p和q在环中的某⼀点相遇,初始时p和q相隔k个节点(0<=k<=n-1,其中n为环中的节点数;图中p和q相隔3个节点,⽽不是2个:因为假如q不动,p需要经过3步移动才可以和q重合),那么可得:t(mod n) = k + 2t(mod n)那么,对任意的正整数m,使得:t + mn = k + 2t那么显然此时 t = mn-k,m>=1。

适合于查找有序单链表的查找方法

适合于查找有序单链表的查找方法

适合于查找有序单链表的查找方法一、引言在计算机科学中,链表是一种常见的数据结构,用于存储和组织数据。

在有序单链表中,数据按照一定的顺序排列,这样可以更快地查找特定的元素。

本文将介绍适用于有序单链表的查找方法,旨在帮助读者理解并运用这些方法。

二、线性查找线性查找是最简单的查找方法之一,它适用于无序和有序的链表。

它的原理是从链表的头部开始,依次比较每个元素,直到找到目标元素或遍历完整个链表。

由于有序单链表的特点是按照顺序排列,线性查找可以在遇到大于目标元素的元素时停止搜索,从而提高效率。

三、二分查找二分查找是一种高效的查找方法,它适用于有序单链表和数组。

它的原理是将链表分为两部分,然后判断目标元素是否在其中一部分,如果在,则继续在该部分进行二分查找;如果不在,则继续在另一部分进行二分查找。

通过每次将查找范围缩小一半,二分查找可以快速定位到目标元素。

四、插值查找插值查找是一种改进的二分查找方法,它适用于有序单链表和数组。

它的原理是根据目标元素的值与链表中元素的值的比较,计算出一个插值,然后根据插值确定目标元素可能在链表中的位置。

通过不断地缩小查找范围,插值查找可以更快地定位到目标元素。

五、斐波那契查找斐波那契查找是一种改进的二分查找方法,它适用于有序单链表和数组。

它的原理是根据斐波那契数列的性质,将链表分为两部分,然后根据目标元素的值与链表中元素的值的比较,确定目标元素可能在链表中的位置。

通过不断地缩小查找范围,斐波那契查找可以更快地定位到目标元素。

六、跳表跳表是一种特殊的数据结构,它适用于有序单链表。

它的原理是在链表的基础上增加多级索引,通过索引层的跳跃操作,可以快速定位到目标元素所在的区域,然后在区域内进行线性查找。

跳表的时间复杂度为O(log n),比线性查找更高效。

七、二叉查找树二叉查找树是一种常见的数据结构,它适用于有序单链表和数组。

它的原理是将链表中的元素按照一定的规则构建为一棵二叉树,然后通过比较目标元素与树中节点的值的大小关系,逐步缩小查找范围,最终找到目标元素。

请写出floatx与零值比较的if语句

请写出floatx与零值比较的if语句

请写出float x 与“零值”比较的if 语句const float EPSINON = 0.00001;if ((x >= - EPSINON) && (x <= EPSINON)1.new、delete、malloc、free关系delete会调用对象的析构函数,和new对应。

free只会释放内存,new调用构造函数。

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。

它们都可用于申请动态内存和释放内存。

对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。

对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。

由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。

注意new/delete不是库函数。

2.delete与delete [ ]区别delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。

在More Effective C++中有更为详细的解释:“当delete操作符用于数组时,它为每个数组元素调用析构函数,然后调用operatordelete来释放内存。

”delete与New配套,delete []与new []配套MemTest *mTest1=new MemTest[10];MemTest *mTest2=new MemTest;Int *pInt1=new int[10];Int *pInt2=new int;delete[]pInt1; //-1-delete[]pInt2; //-2-delete[]mTest1;//-3-delete[]mTest2;//-4-在-4-处报错。

3计算机招聘笔试试题精华

3计算机招聘笔试试题精华

试题6:void GetMemory( char **p, int num ){*p = (char *) malloc( num );}void Test( void ){char *str = NULL;GetMemory( &str, 100 );strcpy( str, "hello" );printf( str );free(str);str=NULL;}答:malloc后,应判断*p是否NULL标准的new运算符在分配失败时,抛出一个bad_alloc类型的异常。

在任何情况下,校验标准形式new运算符返回结果都不能起到检测错误的功效。

void test(void){char str = (char ) malloc(100)strcpy(str, "hello");free(str);if(str != null){strcpy(str “world”);//野指针printf(str)}}请问运行test函数会有什么样的结果?篡改动态内存区的内容,后果难以预料,非常危险。

因为free(str) 之后,str成为野指针,if(str != null)语句不起作用。

如果数组做函数形参,那么就蜕变为普通指针(不是指针常量),而且失去了常量性,可以被修改void test(char str[100]) {str++;}试题7:编写类String的构造函数、析构函数和赋值函数答:class String {public:String(const char *str = NULL);String(const String &other);~String();String & operator= (const String &other);private:char *m_data;};String::String(const char *str) {if(str == NULL) {m_data = new char[1];//arrary new}else {m_data = new char[strlen(str) + 1];strcpy(m_data, str);}}String::String(const String &other) {m_data = new char[strlen(other.m_data) + 1];strcpy(m_data, other.m_data);}String::~String() {delete[] m_data;//array delete}String & String::operator= (const String &other) {if(this == &other)return (*this);delete[] m_data;m_data = new char[strlen(other.m_data) + 1];strcpy(m_data, other.m_data);return (*this);}试题8:请说出C++中static和const关键字尽可能多的作用答:static的作用:1)static局部变量,作用域为本函数内,但是生存期是整个程序运行期,不同于auto变量,只初始化一次,下次调用时维持上次的值。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

给定一个单链表,只给出头指针h:
1、如何判断是否存在环?
2、如何知道环的长度?
3、如何找出环的连接点在哪里?
4、带环链表的长度是多少?
解法:
1、对于问题1,使用追赶的方法,设定两个指针slow、fast,从头指针开始,每次分别前进1步、2步。

如存在环,则两者相遇;如不存在环,fast遇到NULL退出。

2、对于问题2,记录下问题1的碰撞点p,slow、fast从该点开始,再次碰撞所走过的操作数就是环的长度s。

3、问题3:有定理:碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。

(证明在后面附注)
4、问题3中已经求出连接点距离头指针的长度,加上问题2中求出的环的长度,二者之和就是带环单链表的长度
void Isloop(Llink head)
{
if(!head||!head->next)
return;
Llink p,q;
bool loop=false;
p=q=head->next;
while(q&&q->next)//判断是否有环
{
p=p->next;
q=q->next->next;
if(p==q)
{
loop=true;
break;
}
}
if(!loop)
cout<<"This link has not loop\n";
else
{
cout<<"This link has a loop\n";
Llink r=p;
q=head->next;
int nonloop=1,loopcount=1;
//nonloop计算非环结点数,loopcount计算环上结点数
do//计算环上的结点数
{
p=p->next;
++loopcount;
}while(p!=r);
--loopcount;
while(p!=q)//得到环的入口结点,同时计算得到非环的结点数
{
p=p->next;
q=q->next;
++nonloop;
}
--nonloop;
cout<<"\nStart of loop: "<<p->data<<endl;
cout<<"\nCount of nonloop: "<<nonloop
<<"\nCount of loop: "<<loopcount
<<"\nCount of Linknode:
"<<nonloop+loopcount<<endl;
}
}
判断是否存在环的程序:
bool IsExitsLoop(slist *head)
1{
2 slist *slow = head, *fast = head;
3 while ( fast && fast->next )
4 {
5 slow = slow->next;
6 fast = fast->next->next;
7 if ( slow == fast ) break;
8 }
9 return !(fast == NULL || fast->next == NULL);
10}
寻找环连接点(入口点)的程序:
slist* FindLoopPort(slist *head)
11{
12 slist *slow = head, *fast = head;
13 while ( fast && fast->next )
14 {
15 slow = slow->next;
16 fast = fast->next->next;
17 if ( slow == fast ) break;
18 }
19 if (fast == NULL || fast->next == NULL)
20 return NULL;
21 slow = head;
22 while (slow != fast)
23 {
24 slow = slow->next;
25 fast = fast->next;
26 }
27 return slow;
28}
亦可以用类似与hash表的方法,即设立一个数组,将链表结点中的值做数组下标,当赋值冲突时就是环的接入点
29 bool isloop(Llink p)
{
if(!p||!p->next)
return true;
int a[MAXSIZE],n=0;
memset(a,0,sizeof(int)*MAXSIZE);
p=p->next;
while(p)
{
if(a[p->data]==-1)//存在环时,会发生冲突 {
cout<<"\nLoop node: "<<p->data<<endl <<"\nLen of node: "<<n<<endl;
return true;
}
a[p->data]=-1;
++n;
p=p->next;
}
return false;
}
Llink CreatlinkLoop()
//创建一个有环的链表
{
Llink head=new Lnode;
//head->data=0;
head->next=NULL;
Lelemtype e;
Llink q=head;
int N=0;
cout<<"input elems:";
while(cin>>e)
{
Llink p=new Lnode;
++N;
p->data=e;
p->next=q->next;
q->next=p;
q=p;
}
cin.clear();
cin.sync();
srand(time(0));
q->next=Findnode(head,rand()%N);//随机产生环的接入点
return head;
}
Llink Findnode(Llink head,int n)//找出链表中的第n个结点
{
if(n<=0)
return head;
Llink p=head->next;
for(int i=1;p&&i<n;++i)
p=p->next;
return p;
}
///////////////////////////////////////////////// ///////
附注
问题2的证明如下:
链表形状类似数字 6 。

假设甩尾(在环外)长度为 a(结点个数),环内长度为 b 。

则总长度(也是总结点数)为 a+b 。

从头开始,0 base 编号。

将第 i 步访问的结点用 S(i) 表示。

i = 0, 1 ...
当 i<a 时,S(i)=i ;
当i≥a 时,S(i)=a+(i-a)%b 。

分析追赶过程:
两个指针分别前进,假定经过 x 步后,碰撞。

则有:
S(x)=S(2x)
由环的周期性有:2x=tb+x 。

得到 x=tb 。

另,碰撞时,必须在环内,不可能在甩尾段,有 x>=a 。

连接点为从起点走 a 步,即 S(a)。

S(a) = S(tb+a) = S(x+a)。

得到结论:从碰撞点 x 前进 a 步即为连接点。

根据假设易知 S(a-1) 在甩尾段,S(a) 在环上,而 S(x+a) 必然在环上。

所以可以发生碰撞。

而,同为前进 a 步,同为连接点,所以必然发生碰撞。

综上,从 x 点和从起点同步前进,第一个碰撞点就是连接点。

/////////////////////////////////////////////////////////////
假设单链表的总长度为L,头结点到环入口的距离为a,环入口到快慢指针相遇的结点距离为x,环的长度为r,慢指针总共走了s步,则快指针走了2s步。

另外,快指针要追上慢指针的话快指针至少要在环里面转了一圈多(假设转
了n圈加x的距离),得到以下关系:
s = a + x;
2s = a + nr + x;
=>a + x = nr;
=>a = nr - x;
由上式可知:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点,搞掂!附图:
临沂网站优化 Gz5ScWl3488n。

相关文档
最新文档