一、链表
1、单链表的数据结构
链表是一种物理存储单元上非连续、非顺序的存储结构。
链表中每个元素包含一个称为节点的结构;节点 = 数据域 + 引用域。 * 当向链表新添加一个元素的时候,就会产生一个包含它的节点。 每个节点和它相邻的节点通过引用域相连接。
class Node<E>{
protected E element; //数据域
protected Node<E> next; //引用域
public Node(E data){
this.element = data;
}
}
二、单链表的相关练习
创建链表
class SingleLinkedList<E>{
protected Node<E> head; //永远指向第一个有效节点
//节点类
class Node<E>{
protected E element; //数据域
protected Node<E> next;//引用域,用来连接链表中的节点
public Node(E data){
this.element = data;
}
}
public Node<E> createNode(E data){
return new Node(data);
}
//添加
public void add(E data){
//创建一个新的节点
Node<E> newNode = new Node<>(data);
//分情况:空链表 head == null
if(head == null){
head = newNode;
}else{
//链表不为空,遍历至尾节点
Node<E> tmp = head;
while(tmp.next != null){
tmp = tmp.next;
}
//绑定新节点
tmp.next = newNode;
}
}
//添加一个新节点至指定位置
public boolean addPos(E data, int pos){
if(pos < 0 || pos > getLength()+1){
return false;
}
//创建新节点
Node<E> newNode = new Node<>(data);
//插入0号位置需要特殊处理
if(pos == 0){
newNode.next = head;
head = newNode;
return true;
}
//找到pos位置
Node<E> tmp = head;
for(int i=0; i<pos-1; i++){
tmp = tmp.next;
}
//tmp指向pos-1位置的节点
//绑定新节点
newNode.next = tmp.next;
tmp.next = newNode;
return true;
}
public boolean delete(E data){
//head节点需要单独处理
if(head.element == data){
head = head.next;
return true;
}
//删除某一个正常节点
Node<E> tmp = head;
while(tmp.next != null){
if(tmp.next.element == data){
tmp.next = tmp.next.next;
return true;
}
tmp = tmp.next;
}
return false;
}
public int getLength(){
int length = 0;
Node<E> tmp = head;
while(tmp != null){
length ++ ;
tmp = tmp.next;
}
return length;
}
public Node<E> getHead() {
return head;
}
public void setHead(Node<E> head) {
this.head = head;
}
public void show(){
Node<E> tmp = head;
while(tmp != null){
System.out.print(tmp.element + " ");
tmp = tmp.next;
}
System.out.println();
}
}
1、逆序输出链表
//方法一
public static <E> void reverseNum(SingleLinkedList<E>.Node<E> head){
Stack<E> stack = new Stack<>();
SingleLinkedList<E>.Node<E> tmp = head;
while(tmp != null){
stack.push(tmp.element);
tmp = tmp.next;
}
while (!stack.isEmpty()){
System.out.print(stack.pop()+" ");
}
}
//方法二
public static <E> void reversePrintList(SingleLinkedList<E>.Node<E> head){
//递归终止条件
if(head == null){
return;//处理办法
}
//提取重复逻辑,缩小问题规模
reversePrintList(head.next);
System.out.println(head.element + " ");
}
2、逆置链表
//逆序输出单链表
public static <E> void reversePrintList(SNode<E> head){
//递归终止条件
if(head == null){
return;//处理办法
}
//提取重复逻辑,缩小问题规模
reversePrintList(head.next);
System.out.println(head.element + " ");
}
//逆置单链表
public static <E> Node<E> reverseList(Node<E> head){
Node<E> current = head; //当前节点
Node<E> prev = null;//当前节点的前一个
Node<E> newHead = null;//逆置后链表的头节点
while(current != null){
//当前节点的下一个
Node<E> next = current.next;
if(next == null){
newHead = current;
}
current.next = prev;
prev = current;
current = next;
}
return newHead;
}
3、合并两个有序的单链表,保证合并之后依然有序
public static <E extends Comparable<E>> SingleLinkedList<E>.Node<E> mergeLinkedList(
SingleLinkedList<E>.Node<E> head1, SingleLinkedList<E>.Node<E> head2){
//确定新链表的头节点
SingleLinkedList<E>.Node<E> curHead = null;
if(head1.element.compareTo(head2.element) < 0) {
curHead = head1;
head1 = head1.next;
}else{
curHead = head2;
head2 = head2.next;
}
SingleLinkedList<E>.Node<E> tmp = curHead;
while(head1 != null && head2 != null){
if(head1.element.compareTo(head2.element) < 0){
tmp.next = head1;
head1 = head1.next;
}else{
tmp.next = head2;
head2 = head2.next;
}
tmp = tmp.next;
}
if(head1 == null){
tmp.next = head2;
}
if(head2 == null){
tmp.next = head1;
}
return curHead;
}
2、查找单链表中倒数第K个元素
public static <E> SingleLinkedList<E>.Node<E> lastK(
SingleLinkedList<E>.Node<E> head, int k){
if(head == null || k<0) return null;
//控制时间复杂度为O(n),只需要遍历链表一次实现
SingleLinkedList<E>.Node<E> front = head;
SingleLinkedList<E>.Node<E> behind = head;
for(int i=0; i<k-1; i++){
if(front.next != null){
front = front.next;
}else{
return null;
}
}
while(front.next != null){
front = front.next;
behind = behind.next;
}
return behind;
}
3、不允许遍历链表, 在 pos之前插入
public static <E> boolean insertPosBefore(
SingleLinkedList<E> list,SingleLinkedList<E>.Node<E> pos, E data){
if(list.getHead() == null || pos == null) return false;
//创建新节点
SingleLinkedList<E>.Node<E> newNode = list.createNode(pos.element);
//插入新节点至pos之后
newNode.next = pos.next;
pos.next = newNode;
//改变pos位置的element为data
pos.element = data;
return true;
}
4、两个链表相交,输出相交节点
public static <E> SingleLinkedList<E>.Node<E> commonNode(
SingleLinkedList<E> list1, SingleLinkedList<E> list2){
if(list1.getHead() == null || list2.getHead() == null) return null;
//计算两个链表的差值
int length1 = list1.getLength();
int length2 = list2.getLength();
int lengthDif = Math.abs(length1-length2);
SingleLinkedList<E>.Node<E> longHead = list1.getHead();
SingleLinkedList<E>.Node<E> shortHead = list2.getHead();
if(length1 < length2){
longHead = list2.getHead();;
shortHead = list1.getHead();
}
//长链表先走
for(int i=0; i<lengthDif; i++){
longHead = longHead.next;
}
//两个指针同时走
while(longHead != shortHead){
longHead = longHead.next;
shortHead = shortHead.next;
}
return longHead;
}
5、单链表是否有环,环的入口节点是哪个
public static <E> SingleLinkedList<E>.Node<E> ringNode(
SingleLinkedList<E>.Node<E> head){
//判断是否存在环,如果存在,则已知环中任意节点
SingleLinkedList<E>.Node<E> meeetingNode = isRing(head);
//说明环不存在
if(meeetingNode == null){
return null;
}
//计算环中节点的个数 length
int length = 0;
while (meeetingNode.next == meeetingNode){
meeetingNode = meeetingNode.next;
length++;
}
SingleLinkedList<E>.Node<E> slow = head;
while (meeetingNode == slow){
meeetingNode = meeetingNode.next;
slow = slow.next;
}
return null;
}
public static <E> SingleLinkedList<E>.Node<E> isRing(SingleLinkedList<E>.Node<E> head){
if(head == null) return null;
SingleLinkedList<E>.Node<E> slow = head.next;
//链表中只有一个头节点
if(slow == null) return null;
SingleLinkedList<E>.Node<E> fast = slow.next;
while(fast != null && slow != null && fast.next != null){
if(fast == slow){
return fast;
}
slow = slow.next;
fast = fast.next.next;
}
return null;
}