public class LinkList {
Node first;//指向第一个结点
Node last;//指向最后一个结点
class Node {//使用内部类来构造链表
Node next;//外部类实例化内部类能访问私有属性,
int value;//这里没有设置为私有属性是因main函数测试的时候方便。
public Node(){};//空构造函数
public Node(int value) {//构造函数
this.value = value;
}
}
}
/**
* 使用尾插法创建链表
* @param value
*/
public void Construct(int value) {
if(first == null) {
Node current = new Node(value);
first = current;
last = first;//只有一个节点的时候,最后一个节点也是第一个节点
}else {
Node current = new Node(value);
last.next = current;//最后一个节点指向新节点
last = current;//更新最后一个节点的位置
}
}
public void travering(Node first) {//输入头节点
Node traver = first;
while(traver!=null) {//只要节点不为空就说明还没遍历完
System.out.print(traver.value+" ");
traver = traver.next;
}
System.out.println();
}
/**
* 使用尾插法插入数据
* 当然也可以把数据往前插,用first来实现.insertNode.next = first; first = insertNode;
* 不过我喜欢插后面~
* @param value
*/
public void Insert(int value) {
Node insertNode = new Node(value);
if(last.next == null) {//判断last是否指向了最后一个结点
last.next = insertNode;//最后一个结点指向新结点
last = insertNode;//更新最后一个结点
}
}
/**
* 删除指定数值的结点(只删除一个),但是要考虑到 删除头节点的情况
* 删除的方式是用目标结点的前一个结点指向目标结点的下一个结点,java会自动回收删除的结点空间
* @param value
*/
public void Delete(int value) {
if(first.value == value)//头节点就是目标结点情况
first = first.next;
else {
Node targetNodeBefore = first;//目标结点的前一个结点
Node targetNode = targetNodeBefore.next;//目标结点
while(targetNode.value!=value) {
targetNodeBefore = targetNode;
targetNode = targetNode.next;
}
targetNodeBefore.next = targetNode.next;//删除目标结点
}
}
/**
* 求链表的长度
* 遍历一遍链表,指针挪动一次计数数值+1
*/
public int ListLength() {
Node traverNode = first;
int length = 0;
while(traverNode!=null) {//判断链表结束条件
length++;
traverNode = traverNode.next;//往下挪动
}
return length;
}
/**
* 求倒数第k个节点
* 思路:遍历求整体长度,再定位到指定节点,时间复杂度为O(n)
*/
public Node SearchK(int k) {
Node kNode = first;
int length = ListLength();//调用上面写的函数
for(int i=0;i
思路:创建两个节点,我们称之为第一个结点和第二个结点。使它们的距离为k-1(第一个结点不动,第二个结点往后移动K-1个位置。由于第二个节点判定到链表终点的条件是traverNode.next == null的情况,但是第二个节点并不会走到null,所以第二个结点最终会停留在链表的最后一个节点,属于倒数第一个节点。因此它和第一个结点的距离就是K-1),然后两个节点同时往后面移动,直到第二个节点到达最后的结点,那么第一个结点就是倒数第k个结点。
/**
* 求倒数第k个节点
* 实际上还是遍历了一遍链表。时间复杂度为O(n)
*/
public Node SearchKplus(int k) {
if(k==0)
return null;
Node kNode = first;
Node traverNode = first;
for(int i=0;i
/**
* 查找链表的中间节点,不使用遍历得到链表长度
*
*/
public Node FindMid() {
Node midNode = first;
Node lastNode = first;
while(lastNode.next!=null) {
if(lastNode.next.next!=null) {
midNode = midNode.next;
lastNode = lastNode.next.next;
}else {
break;
}
}
System.out.println(midNode.value);
return midNode;
}
/**
* 递归从尾部输出链表
* 使用到了递归
*/
public void Recursive(Node first) {
if(first.next!=null)
Recursive(first.next);
System.out.print(first.value+" ");
}
/**
* 反转链表
*/
public Node ReverseList(Node first) {
if(first.next == null || first == null)//作用1:如果链表长度为1或0,则直接返回就可以
return first; //作用2:当递归到最后一个节点的时候返回最后一个节
else {
Node newfirst = ReverseList(first.next);//此处的newfirst是反转后的链表的头节点,用来做递归的反复返回和接收,实际其值为不变的
first.next.next = first;//这里的first是从倒数第二个节点开始,意思是当前结点的下一个节点指向当前结点
first.next = null;//然后当前结点指向null,从而实现链表的反转
return newfirst;
}
}
/**
* 成圆环函数
*/
public void MkCircle(Node first) {
last.next = first;//这种情况是整个链表就是一个环
//last.next = first.next;//可以通过定位到下一个节点来实现自己想要的环
}
思路:使用两个指针去遍历链表,第一个指针一次挪动一个位置,第二个指针一次挪动两个位置,如果改链表有环,那么他们最终会相遇
/**
* 判断链表是否有环
*/
public Node Hascircle(Node head) {
Node first = head;
Node second = head;
while(second.next!=null) {
first = first.next;
second = second.next.next;
if(first == second)
return first;
}
return null;
}
思路:拿到上个函数返回的结点,用这个结点去遍历直到返回初始位置,那就是环的长度了
/**
* 判断环的长度
*/
public int LengthOfCircle(Node first) {
Node tNode = null;
Node firstNode = null;
Integer length = 0;
if(Hascircle(first)!=null){
tNode = Hascircle(first).next;//为了不和下面的判断条件起冲突,先往下挪一个位置
length++;//对应环的长度要加一
firstNode = Hascircle(first);//记录初始结点
while(tNode!=firstNode) {
length++;
tNode=tNode.next;
}
return length;
}else {
return length;
}
}
思路:同样是两个指针,第二个指针先走一个环的长度,然后两个指针再一起往下遍历,直至两个指针相等,那这就是环的初始结点
/**
* 找出环的初始节点
* 思路:设置两个头节点开始遍历,第二个节点比第一个节点先走一个环的长度,然后再同时往下一步一步走,最终相等的那个节点就是环的初始节点
*/
public Node firstCircleNode(Node first) {
Node firstNode = first;
Node secondNode = first;
for(int i=0;i
思路:正常做我们删除节点需要得到目标节点的上一个节点,按照正常思路我们需要去遍历链表,则时间复杂度为O(n)。这里我们将节点1后面的节点复制上来,然后删除节点1后面的节点。
public void DeleteTargetNode(Node first,Node targetNode) {
if(targetNode.next == null)//删除节点为最后一个节点,这时只能采用常规方法来操作
System.out.println("删除节点为最后一个节点,请采用常规方法来操作,复杂度为O(n)");
else {
targetNode.value = targetNode.next.value;
targetNode.next = targetNode.next.next;
}
}