单链表的理解
单链表在我们java中是一个重要的数据结构,与数组类似,都是用来储存数据的,但是他是一个个的数据链起来,用一组地址任意的存储单元存放线性表中的数据元素,如下图:
我们自定一个Entry类,里面有两个属性((逻辑地址相连,物理地址不相连)):
int data; //要存放的数据
Entry next; //指向下一个节点地址的next域,相当于指针
自己实现一个单链表的
class TestLink {
//节点类
class Entry {
int data;
Entry next;
//头节点
public Entry() {
this.data = -1;
this.next = null;
}
//得到实体节点的构造函数
public Entry(int data) {
this.data = data;
this.next = null;
}
}
//标志单链表的头部
private Entry head = null;
public Entry getHead() {
return this.head;
}
public TestLink() {
this.head = new Entry();
}
public TestLink(Entry newHead) {
this.head = newHead;
}
这样一个简单的单链表就定义好了,下面将实现一些简单的操作:
头插法
头插法相当于把你要插入的元素插入到链表的头节点的后面,相当于有数据的节点的第一个位置,他的逻辑上实现是如图:
代码实现:
/**
* 头插法
* @param val :要插入的元素
*/
public void headInsert(int val) {
//1、得到一个实体节点
Entry entry = new Entry(val);
//2、串起来
entry.next = this.head.next;
this.head.next = entry;
}
尾插发
插入方式与头插法类似,就是把你要插入的元素插入到链表的末尾,如图:
代码实现:
/**
* 尾插发
* @param val : 要插入的元素
*/
public void tailInsert(int val) {
//1、找到尾巴
Entry cur = this.head;
while(cur.next != null) {
cur = cur.next;
}
//2、插入
Entry entry = new Entry(val);
cur.next = entry;
}
插入指定的位置
这也是同样的道理,传入两个参数,一个要插入的位置,一个要插入的元素,先遍历链表找到要插入的位置,然后插入,如图:
代码实现:
/**
* 插入的指定的位置(第一位置下标为0)
* @param val :要插入的元素
* @param pos :要插入的位置
*/
publicvoid insertPos(int val,int pos) {
if(pos < 0 || pos >= getLength()+1) {
return false;
}
Entry cur = this.head;
for(int i = 0;i <= pos-1;i++) {
cur = cur.next;
}
Entry entry = new Entry(val);
entry.next = cur.next;
cur.next = entry;
删除元素
单链表的删除机制与数组不同,他是直接把要删除的元素的节点与前后连接的方式直接删除,如图:
代码实现:
/**
* 删除节点值为key的元素
* @param key 要删除的元素
*/
public void deleteKeyAll(int key) {
Entry pre = this.head;
Entry del = this.head.next;
while(del != null) {
if(del.data == key) {
pre.next = del.next;
del = del.next;
}else {
pre = del;
del = del.next;
}
}
}
获得单链表的长度
/**
* 获取单链表的长度
* @return
*/
public int getLength() {
int len = 0;
Entry cur = this.head.next;
while (cur != null) {
len++;
cur = cur.next;
}
return len;
}
打印单链表
public void show() {
Entry cur = this.head.next;
while(cur != null) {
System.out.print(cur.data+" ");
cur = cur.next;
}
System.out.println();
}
单链表的逆置
public void reversLink() {
Entry cur = this.head.next;
Entry curNext = null;
this.head.next = null;
while(cur != null) {
curNext = cur.next;
cur.next = this.head.next;
this.head.next = cur;//头插法
cur = curNext;
}
求倒数第k个节点的data值
定义两个引用,将第一个先走k-1步,接下来两个同时向后遍历。直到第一个指向最后一个,另一个就指向倒数第k个:
public int lastK(int k) {
if(k < 0 || k > getLength()) {
return -1;
}
Entry entry1 = this.head;
Entry entry2 = this.head;
while(k-1> 0) {
if(entry2.next != null) {
entry2 = entry2.next;
k--;
}else {
return -1;
}
}
while(entry2.next != null) {
entry1 = entry1.next;
entry2 = entry2.next;
}
return entry1.data;
}
判断一个单链表是否有环,求环入口点,求环的长度
环的形成如图:
首先,先创建一个环:
public void createLoop() {
Entry cur = this.head;
while(cur.next != null) {
cur = cur.next;
}
cur.next = this.head.next.next;
}
判断是否有环:
创建两个引用,一个走一步,一个走两步,如果两个相遇,就说明有环
public boolean isLoop() {
Entry fast = this.head;
Entry slow = this.head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
return true;
}
}
return false;
}
求环的入口点:
当第一次相遇后一个引用留在相遇点,另一个引用回到原点。同时出发保持相同速度下次遇见的地方就是环的入口点,
public int enterEntry() {
Entry fast = this.head;
Entry slow = this.head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
break;
}
}
slow = this.head;
while(slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow.data;
}
求环的长度:
public int getLoopLen() {
Entry fast = this.head;
Entry slow = this.head;
int len = 0;
boolean first = false;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow && first == true) {
break;
}
if(fast == slow && first == false) {
first = true;
}
if(first == true) {
len++;
}
}
return len;
}
判断两个单链表是否相交
将长的链表的引用先走两个链表之差个距离,保证开始走的时候两个引用知道结束走的距离一样
public class TestDemoLink {
/**
* 判断两个单链表(长度不一定)是否相交,相交求交点
* 创建两个单链表
* @param link1
* @param link2
*/
public static void createCutLink(TestLink link1,TestLink link2) {
TestLink.Entry head1 = link1.getHead();
TestLink.Entry head2 = link2.getHead();
head1.next.next.next = head2.next.next;
}
/**
* 判断是否相交 求交点
* @param link1
* @param link2
* @return
*/
public static TestLink.Entry isCut(TestLink link1,TestLink link2) {
int len1 = link1.getLength();
int len2 = link2.getLength();
TestLink.Entry head1 = link1.getHead();
TestLink.Entry head2 = link2.getHead();
int mylen = len1-len2;
if(mylen < 0) {
head1 = link2.getHead();
head2 = link1.getHead();
}
while(mylen != 0) {
head1 = head1.next;
mylen--;
}
while(head1 != head2) {
head1 = head1.next;
head2 = head2.next;
}
if(head1 == head2) {
return head1;
}
return null;
}
合并两个单链表
/**
* 合并两个有序的单链表(递增)
* @param link1
* @param link2
* @return
*/
public static TestLink mergeLink(TestLink link1,TestLink link2) {
TestLink.Entry newHead = null;
TestLink.Entry p1 = link1.getHead().next;
TestLink.Entry p2 = link2.getHead().next;
if(link1.getHead().next.data < link2.getHead().next.data) {
newHead = link1.getHead();
p1 = link1.getHead().next;
}else {
newHead = link2.getHead();
p2 = link2.getHead().next;
}
TestLink.Entry tempHead = newHead;
while(p1 != null && p2 != null) {
if(p1.data < p2.data) {
tempHead.next = p1;
p1 = p1.next;
}else {
tempHead.next = p2;
p2 = p2.next;
}
tempHead = tempHead.next;
}
if(p1 != null) {
tempHead.next = p1;
}
if(p2 != null) {
tempHead.next = p2;
}
return new TestLink(newHead);
}
/**
* 打印合并的单链表
* @param link
*/
public static void showMerge(TestLink link) {
link.show();
}
测试打印结果
public static void main(String[] args) {
TestLink testLink = new TestLink();
for(int i = 0;i < 10;i++) {
//testLink.headInsert(i);//
testLink.tailInsert(i);
}
testLink.show();
TestLink testLink2 = new TestLink();
for(int i = 10;i < 30;i++) {
//testLink2.headInsert(i);//
testLink2.tailInsert(i);
}
testLink2.show();
showMerge( mergeLink(testLink,testLink2) ) ;
createCutLink(testLink,testLink2);
testLink.show();
testLink2.show();
TestLink.Entry entry = isCut(testLink,testLink2);
System.out.println(entry.data);
testLink.createLoop();
if(testLink.isLoop()) {
System.out.println("isLoop");
}
int data = testLink.enterEntry();
System.out.println(data);
int len = testLink.getLoopLen();
System.out.println(len);
}
结果:
C:\java\java7\jdk1.7.0_80\bin\java.exe -javaagent:D:\ideaIU-2018.1.5.win\lib\idea_rt.jar=12791:D:\ideaIU-2018.1.5.win\bin -
0 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
0 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
0 1 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
11
isLoop
1
20
Process finished with exit code 0