单链表
一、单链表的概念
链表是最基本的数据结构,其存储的你原理图如下图
head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,其中的next指向null。
二、用java实现单链表
2.1、编写一个Node类来充当结点的模型。
其中有两个属性,1.存放数据的data。2.存放下一结点的引用。
2.2、单链表的简单操作
三、单链表的常见题型
3.1、逆序打印单链表
3.2、单链表逆置
3.3、不允许遍历链表,在pos节点之前插入一个新节点
public boolean addPos(T value, int pos){
//new一个新节点 pos-1的节点 在pos-1位置之后链上当前的newNod
Node<T> newNode = new Node(value);
if(pos < 0 || pos >= getLength()){
return false;
}
if(pos == 0){
newNode.next = head;
head = newNode;
return true;
}
//pos-1位置的节点
Node<T> tmp = head;
for(int i=0; i<=pos -1; i++){
tmp = tmp.next;
}
//i>pos- 1 tmp刚好指向pos-1位置的节点
newNode.next = tmp.next;
tmp.next = newNode;
return true;
}
3.4、查找单链表中倒数第k个节点
3.5、合并两个有序的链表,保证合并之后的链表依然有序![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/22e367ca6fea4ff9a21570c80d0d7c22.png)
```java
```java
class SingleListTestDemo2 {
//合并两个有序的链表,保证合并之后的链表依然有序
//1 3 5 7 9
//2 4 6
//-》1 2 3 4 5 6 7 9
//泛型类型 涉及到比较大小 类型擦除到Object类型,Object类型中没有提供比较大小的方法
//但是Comparable接口提供了,期望类型擦除到Comparable接口
//泛型类型可以定义上界 通过extends关键字 使其擦除到上界所对应的类型
public static <T extends Comparable<T>> MySingleList<T>.Node<T> mergeOrderList(MySingleList<T>.Node<T> head1, MySingleList<T>.Node<T> head2) {
if(head1 == null && head2 == null){
return null;
}else if(head1 == null){
return head2;
}else if(head2 == null){
return head1;
}
//其中就可以使用compareTo方法比较大小
//要求所传的泛型类型也必须要实现
MySingleList<T>.Node<T> newHead = null;
//确定合并后链表的头节点,利用CompareTo方法比较head1与head2头的大小
if (head1.getData().compareTo(head2.getData()) >= 0) {
//head1头大于head2头新链表头指定为head2
newHead = head2;
//head2头往下走
head2 = head2.getNext();
} else {
newHead = head1;
head1 = head1.getNext();
}
//遍历list1和list2,将对应位置更小的节点链到新链表之后
//一旦list1和list2其中一个遍历完成,则直接链入另外一个未遍历完的链表
MySingleList<T>.Node<T> tmp = newHead;
while (head1 != null && head2 != null) {
if (head1.getData().compareTo(head2.getData()) >= 0) {
//head2指向新链表尾部
tmp.setNext(head2);
//head2为更小的位置点,head2需要往后走
head2 = head2.getNext();
//tmp永远指向新链表尾巴
tmp = tmp.getNext();
} else {
tmp.setNext(head1);
//head1为更小的位置点,head1需要往后走
head1 = head1.getNext();
//tmp永远指向新链表尾巴
tmp = tmp.getNext();
}
}
if (head1 == null) {
tmp.setNext(head2);
}
if (head2 == null) {
tmp.setNext(head1);
}
return newHead;
}
public static void main(String[] args) {
MySingleList<Integer> list1 = new MySingleList<>();
MySingleList<Integer> list2 = new MySingleList<>();
list1.addTail(1);
list1.addTail(5);
list1.addTail(6);
list1.addTail(8);
list1.addTail(10);
list1.addTail(11);
list1.addTail(15);
list2.addTail(2);
list2.addTail(4);
list2.addTail(6);
list2.addTail(8);
System.out.println(list1.toString());
System.out.println(list2.toString());
MySingleList<Integer>.Node<Integer> newHead = mergeOrderList(list1.getHead(), list2.getHead());
//打印新的链表
if(newHead != null){
//定义一个tmp保存newHead链表
MySingleList<Integer>.Node<Integer> tmp = newHead;
while(tmp != null){
System.out.print(tmp.getData()+" ");
tmp = tmp.getNext();
}
System.out.println();
}
}
}
3.6、两个链表相交,求出相交节点
1、两个链表相交,求出相交节点
public class TestDemo12_10 {
public static <T> MySingleList<T>.Node<T> commonNode1(MySingleList<T>.Node<T> head1, MySingleList<T>.Node<T> head2){
if(head1 == null || head2 == null){
return null;
}
//将两个链表中的元素进行入栈操作
Stack<MySingleList<T>.Node<T>> stack1 = new Stack<>();
Stack<MySingleList<T>.Node<T>> stack2 = new Stack<>();
//list1
MySingleList<T>.Node<T> tmp = head1;
while(tmp != null){
stack1.push(tmp);
tmp = tmp.getNext();
}
//list2
tmp = head2;
while(tmp != null){
stack2.push(tmp);
tmp = tmp.getNext();
}
MySingleList<T>.Node<T> commonNode = null;
//判断栈顶元素是否相等
while(stack1.peek() == stack2.peek()){
//给定一个参数保存栈顶元素
commonNode = stack1.peek();
stack1.pop();
stack2.pop();
}
//返回相交节点
return commonNode;
}
public static void main(String[] args) {
//构造两个相交的链表
MySingleList<Integer> list1 = new MySingleList<>();
MySingleList<Integer> list2 = new MySingleList<>();
list1.addTail(1);
list1.addTail(10);
list1.addTail(5);
list1.addTail(7);
list1.addTail(6);
list1.addTail(19);
list1.addTail(20);
MySingleList<Integer>.Node<Integer> tmp = list1.getHead();
MySingleList<Integer>.Node<Integer> commonNode = null;
//先找到你要相交的节点,比如6号元素所在节点,那你就找datd域为6的这个节点
while(tmp != null){
if(tmp.getData() == 6){
commonNode = tmp;
break;
}
tmp = tmp.getNext();
}
list2.addTail(3);
list2.addTail(5);
tmp = list2.getHead();
//找到list1的尾节点
while(tmp.getNext() != null){
tmp = tmp.getNext();
}
//在当前链表尾节点之后拼接上这个6号节点
tmp.setNext(commonNode);
System.out.println(list1.toString());
System.out.println(list2.toString());
MySingleList<Integer>.Node<Integer> result1 = commonNode1(list1.getHead(), list2.getHead());
if(result1 != null ){
System.out.println(result1.getData());
}
}
}
3.7、判断链表是否带环,并求环的入口点
/**
* 判断一个链表是否有环,求出环的入口节点
*/
public class TestDemo12_10 {
//判断是否有环
public static <T> MySingleList<T>.Node<T> isRing(MySingleList<T>.Node<T> head){
if(head == null){
return null;
}
//定义快慢指针
MySingleList<T>.Node<T> slow = head.getNext();
//链表中只有一个节点
if(slow == null){
return null;
}
MySingleList<T>.Node<T> fast = slow.getNext();
while(fast != null && fast.getNext() != null && slow != null){
//快指针和慢指针指向相同的节点(相遇)
if(slow == fast){
return slow;
}
//快指针走两步,慢指针走一步
slow = slow.getNext();
fast = fast.getNext().getNext();
}
return null;
}
//求环的入口节点
public static <T> MySingleList<T>.Node<T> entranceNode(MySingleList<T>.Node<T> head){
//meetingNode存在于环中
MySingleList<T>.Node<T> meetingNode = isRing(head);
//计算环中节点的个数
int length = 1;
MySingleList<T>.Node<T> tmp = meetingNode;
//走一圈判断环的长度
while(tmp.getNext() != meetingNode){
tmp = tmp.getNext();
length++;
}
//类比求倒数第k个节点的算法,相当于数学里面的追击问题
//front先走length front, behind开始同时走 front != behind
MySingleList<T>.Node<T> front = head;
MySingleList<T>.Node<T> behind = head;
for(int i=0; i<length ;i++){
front = front.getNext();
}
//front和behind同时走
while(front != behind){
front = front.getNext();
behind = behind.getNext();
}
return front;
}
public static void main(String[] args) {
//构造一个带环的单链表
MySingleList<Integer> list = new MySingleList<>();
list.addTail(10);
list.addTail(1);
list.addTail(0);
list.addTail(9);
list.addTail(4);
list.addTail(15);
list.addTail(20);
list.addTail(6);
list.addTail(8);
MySingleList<Integer>.Node<Integer> tmp1 = list.getHead();
//找到链表尾部
while(tmp1.getNext() != null){
tmp1 = tmp1.getNext();
}
MySingleList<Integer>.Node<Integer> tmp2 = list.getHead();
//指定环节点
while(tmp2.getData() != 4){
tmp2 = tmp2.getNext();
}
//将指定的环节点插入到链表尾部形成环
tmp1.setNext(tmp2);
MySingleList<Integer>.Node<Integer> entranceNode = entranceNode(list.getHead());
if(entranceNode != null){
System.out.println("环的入口节点:"+entranceNode.getData());
}
}
}