单链表的实现
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
存储原理图
- 1 单链表的构造以及常用的成员方法
- 2 判断单链表是否有环问题
- 3 判断单链表是否相交问题
- 4 寻找倒数第K个节点
1 单链表的构造以及常用的成员方法
//先构造一个节点类,并给出该类相关的成员变量和成员方法
class Node{
int value;
Node next;
public Node(){
this(0);
}
public Node (int value){
this.value = value;
this.next = null;
}
}
//利用节点类,构造链表类
class LinkList{
Node head;
public LinkList(){
head = new Node();
}
//判空
public boolean empty(){
return head.next == null;
}
//头插法
public void insertHead(int val){
Node s = new Node(val);
s.next = head.next;
head.next = s;
}
//尾插法
public void insertTail(int val){
Node n = head;
while (n.next != null){
n = n.next;
}
n.next = new Node(val);
}
//删除一个节点
public void destroy(int value){
Node pcur = head.next;
Node ppre = head;
while(pcur != null){
if(pcur.value == value){
ppre.next = pcur.next;
break;
}
ppre = pcur;
pcur = pcur.next;
}
}
//打印
public String toString(){
StringBuilder builder = new StringBuilder();
Node n = head.next;
while(n != null){
builder.append(n.value+" ");
n = n.next;
}
return builder.toString();
}
}
2 判断单链表是否有环问题
(1) 判断当前单链表是否有环 true:有环 false:没有环
思路方法:定义两个快慢指针,步长为一和二,从头结点出发,当相遇时,说明有环;否则没有环
public boolean haveCircle(){
if(this.head ==null || this.head.next==null) return false;
Node p = this.head;
Node q = this.head;
while(p!=null || q!=null)
{
p = p.next;
q = q.next.next;
if(p == q) return true;
}
return false;
}
(2) 判断单链表是否有环,如果有,需要返回环的入口节点
思路方法:找到快慢指针相遇的节点,该节点到入口节点的距离与头节点到入口节点的距离相等
public Node firstCircleNode(){
if(this.head ==null || this.head.next==null) return null;
Node p = this.head;
Node q = this.head;
while(p!=null || q!=null)
{
p = p.next;
q = q.next.next;
if(p==q){
q = this.head;
while(p != q){
p = p.next;
q = q.next;
}
if(p == q){
return p;
}
}
}
return null;
}
(3) 判断单链表是否有环,如果有,返回环的节点个数
思路方法:入口结点出发,进行计数,再次回到入口节点时结束
public int getCircleNodeNum(){
Node p = firstCircleNode();
if(p == null) return 0;
Node q = p.next;
int length = 1;
while(p!=q)
{
length++;
q = q.next;
}
return length;
}
3 判断单链表是否相交 true:相交 false:不相交
思路方法:求出两单链表长度的差,长的链表先遍历,从而可以让两链表从同一起点长度开始遍历,若能找出相同的节点说明相交,否则不想交。
public Node isLinkCross(LinkList list){
Node p = this.head;
Node q = list.head;
if (p == null || q == null) return null;
int length1 = 0;
int length2 = 0;
while(p != null){
length1++;
p = p.next;
}
while(q != null){
length2++;
q = q.next;
}
p = this.head;
q = list.head;
if(length1 > length2){
for(int i =0;i<length1-length2;i++){
p = p.next;
}
while(p != null && q!= null){
if(p.value == q.value){
break;
}
p = p.next;
q = q.next;
}
return p;
}
else{
for(int i =0;i<length2-length1;i++){
q = q.next;
}
while(p != null && q!= null){
if(p.value == q.value){
break;
}
p = p.next;
q = q.next;
}
return p;
}
}
4 寻找倒数第K个节点 没有返回null
思路方法:先求出节点个数,遍历时的次数即为length-k+1
public Node findReverseKNode(int k){
Node p = this.head;
if (empty()) return null;
int length=0;
while(p.next!=null){
length++;
p = p.next;
}
if (k==0 || k>length) return null;
p = this.head;
for (int i=0;i<length-k+1;i++)
{
p = p.next;
}
return p;
}
代码测试
public class TestLinklist {
public static void main(String[] args) {
LinkList s = new LinkList();//初始化
for(int i = 1;i<10;i++){
s.insertHead((int)(Math.random()*100)); //头插法生成单链表
}
System.out.println(s.toString());//打印
System.out.println("倒数第三个节点值为:" + s.findReverseKNode(3).value);
//将链表最后一个节点与第三个节点链接,构成环;
Node p = s.head;
Node q = s.head.next.next.next;
while(p.next != null){
p = p.next;
}
p.next = q;
System.out.println("入口节点为:"+s.firstCircleNode().value);
System.out.println("节点数为:"+s.getCircleNodeNum());
//构造两个交叉的链表
p.next = null;
LinkList s1 = new LinkList();
Node h = s1.head;
for(int i = 1;i<10;i++){
s1.insertHead((int)(Math.random()*100));
}
while(h.next != null){
h = h.next;
}
h.next = q;
System.out.println(s.toString());
System.out.println(s1.toString());
System.out.println("交叉节点为:"+s.isLinkCross(s1).value);
}
}