单链表
链表:
是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
无头单向非循环链表。
无头双向链表。
- 不支持随机访问
- 任意位置插入删除时间复杂度为O(1)
// 借助链表中的引用次序进行存储
//需要自己定义一个节点类进行存储
//节点类
class ListNode {
public int data;
public ListNode next;
public ListNode(int data) {
this.data = data;
this.next = null;
}
}
class MySingleList {
public ListNode head;//标志头
public MySingleList() {
this.head = null;
}
//头插法
public void addFirst(int data){
ListNode node = new ListNode(data);
if(this.head == null) {
this.head = node;
}else {
node.next = this.head;
this.head = node;
}
}
//尾插法
public void addLast(int data) {
ListNode node = new ListNode(data);
ListNode cur = this.head;
//0、判断是否是第一次插入
if(this.head == null) {
this.head = node;
}else {
//1、找尾巴
while (cur.next != null) {
cur = cur.next;
}
//2、进行插入
cur.next = node;
}
}
/**
* 找到index-1位置的节点 返回当前节点的引用
* @param index
* @return
*/
//找前驱
private ListNode searchIndex(int index) {
//prev-->index-1;
ListNode prev = this.head;
int count = 0;
while (count < index-1) {
prev = prev.next;
count++;
}
return prev;
}
//插入到index位置
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data){
//下标不合法
if(index < 0 || index > getLength()) {
return false;
}
//头插法
if(index == 0) {
addFirst(data);
return true;
}
ListNode prev = searchIndex(index);
ListNode node = new ListNode(data);
node.next = prev.next;
prev.next = node;
return false;
}
public int getLength() {
int count = 0;
ListNode cur = this.head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
//打印单链表数据
public void display(){
if(this.head == null) {
return;
}
ListNode cur = this.head;
while (cur != null) {
System.out.print(cur.data+" ");
cur = cur.next;
}
System.out.println();
}
//查找是否包含关键字key是否在单链表当中
public boolean contains1(int key){
ListNode cur = this.head;
while (cur != null) {
if(cur.data == key) {
return true;
}
cur = cur.next;
}
return false;
}
public ListNode contains2(int key){
ListNode cur = this.head;
while (cur != null) {
if(cur.data == key) {
return cur;
}
cur = cur.next;
}
return null;
}
//返回对应值节点的前驱
private ListNode searchPrev(int key) {
ListNode prev = this.head;
while (prev.next != null) {
if(prev.next.data == key) {
return prev;
}
prev = prev.next;
}
return null;
}
//删除第一次出现关键字为key的节点
public void remove(int key){
//1、删除的节点如果是头结点
if(this.head.data == key) {
this.head = this.head.next;
return;
}
//2、找到删除的节点的前驱 如果找不到 返回null
ListNode prev = searchPrev(key);
if(prev == null) {
System.out.println("没有你要删除的节点");
return;
}
ListNode del = prev.next;
//3、进行删除
prev.next = del.next;
}
//删除所有值为key的节点
public void removeAllKey(int key){
ListNode prev = this.head;
ListNode cur = this.head.next;
while (cur != null) {
if(cur.data == key) {
prev.next = cur.next;
cur = cur.next;
}else {
prev = cur;
cur = cur.next;
}
}
if(this.head.data == key) {
this.head = this.head.next;
}
}
public void clear(){
//this.head = null;
while (this.head.next != null) {
ListNode cur = this.head.next;
this.head.next = cur.next;
}
this.head = null;
}
//反转单链表 时间复杂度为O(N),只遍历了一遍单链表
public ListNode reverseList() {
ListNode prev = null;
ListNode cur = this.head;
ListNode newHead = null;
while (cur != null) {
ListNode curNext = cur.next;
if(curNext == null) {
newHead = cur;
}
cur.next = prev;
prev = cur;
cur = curNext;
}
return newHead;
}
//打印反转后的单链表,要重写display
public void display2(ListNode newHead){
if(newHead == null) {
return;
}
ListNode cur = newHead;
while (cur != null) {
System.out.print(cur.data+" ");
cur = cur.next;
}
System.out.println();
}
//返回中间节点
public ListNode middleNode() {
/*
//此方法,获取长度遍历了一遍链表,共遍历了2 次链表,能否遍历一次
int len = getLength()/2;
ListNode cur = this.head;
for (int i = 0; i < len; i++) {
cur = cur.next;
}
return cur;*/
ListNode low = this.head;
ListNode fast = this.head;
while (fast != null && fast.next != null){ //
fast = fast.next.next; //当fast遍历完时,low正好位于中间位置,因low比fast慢一半
low = low.next;
}
return low;
}
//返回倒数第K个节点,
public ListNode findKthToTail1(int k) {
if(k <= 0 ||k > getLength()) { //不想用getLength,会增加时间复杂度
return null;
}
ListNode fast = this.head;
ListNode slow = this.head;
//先让fast走k-1步,然后再和slow一起走,最后当fast为空时,slow即为所求节点
while (k-1 > 0) {
fast = fast.next;
k--;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
//不用getlength来求倒数第K个节点,要求只遍历一次链表
public ListNode findKthToTail(int k) {
//OJ上面的
// if(k <= 0 || head == null) {
// return null;
//}
if(k <= 0) {
return null;
}
ListNode fast = this.head;
ListNode slow = this.head;
while (k-1 > 0) {
if(fast.next != null) { //加约束达成遍历一遍链表的目的
fast = fast.next;
k--;
}else {
System.out.println("没有这个节点");
return null;
}
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
//以x为基准分割链表
public ListNode partition(int x){
ListNode bs = null;
ListNode be = null;
ListNode as = null;
ListNode ae = null;
ListNode cur = this.head;
while (cur != null) {
if(cur.data < x) {
//是不是第一次插入
if(bs == null) {
bs = cur;
be = bs;
}else {
be.next = cur;
be = be.next; //bs.be 区间的端点
}
}else {
//是不是第一次插入
if(as == null) {
as = cur;
ae = cur;
}else {
ae.next = cur;
ae = ae.next;
}
}
cur = cur.next;
}
//第一个区间没有数据
if(bs == null) {
return as;
}
be.next = as;//若as为空或as正常有值
if(as != null) {
ae.next = null;//as不为空需将结尾置为null,没有此句会出现死循环
}
return bs; //返回头节点
}
//7:删除重复节点!!!!!!加入了一个辅助节点
public ListNode deleteDuplication(){
if(this.head == null) {
return null;
}
ListNode cur = this.head;
ListNode newHead = new ListNode(-1); //虚拟节点
ListNode tmp = newHead;
while (cur != null) {
//重复的节点
if(cur.next != null
&& cur.data == cur.next.data) {
//每一次都需要判断cur.next
while (cur.next != null
&&cur.data == cur.next.data) {
cur = cur.next;
}
cur = cur.next;
}else {
tmp.next = cur;
tmp = tmp.next;
cur = cur.next;
}
}
//如果最后的节点也是重复的
// 需要将tmp.next = null;
tmp.next = null;
return newHead.next;
}
//8. 判断一个链表是否为回文结构
public boolean chkPalindrome() {
//两种特殊情况
//没有节点
if(this.head == null) {
return false;
}
//只有一个节点
if(this.head.next == null) {
return true;
}
//1、找到单链表的中间节点
ListNode fast = this.head;
ListNode slow = this.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//循环结束后,slow即为中间节点
//2、反转单链表(反转中间节点的后半部分)
ListNode cur = slow.next;
while (cur != null) {
ListNode curNext = cur.next;
cur.next = slow;
slow = cur;
cur = curNext;
}
//3、fast/slow往前 head往后走 走到中间时会出现重合
while (slow != this.head) {
//
if(slow.data != this.head.data) {
return false;
}
//增加判断偶数的情况
if(this.head.next == slow) {
return true;
}
slow = slow.next;
this.head = this.head.next;
}
return true;
}
//创造一个环
public void creteLoop() {
ListNode cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = this.head.next.next;
}
//10 判断一个链表是否有环
public boolean hasCycle(){
ListNode fast = this.head;
ListNode slow = this.head;
while (fast != null && fast.next!= null) {
fast = fast.next.next;
slow = slow.next;
if(slow == fast) {
break;
}
}
//如果是因遍历结束退出,则没有环结构
if(fast == null || fast.next == null) {
return false;
}
return true;
}
//返回入环节点
public ListNode detectCycle() {
ListNode fast = this.head;
ListNode slow = this.head;
while (fast != null && fast.next!= null) {
fast = fast.next.next;
slow = slow.next;
if(slow == fast) {
break;
}
}
if(fast == null || fast.next == null) {
return null;//无环
}
//有环
slow = this.head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
测试:
package SingleList;
/**
* Created with IntelliJ IDEA.
* Description:
* User: GAOBO
* Date: 2019-11-03
* Time: 11:35
*/
public class TestSingleList {
// 将两个有序单链表 合并称为一个新的有序链表 并返回
public static ListNode mergeTwoLists(ListNode headA, ListNode headB){
ListNode newHead = new ListNode(-1); //����ڵ�
ListNode tmp = newHead;
while (headA != null && headB != null) {
if(headA.data < headB.data) {
tmp.next = headA;
headA = headA.next;
tmp = tmp.next;
}else {
tmp.next = headB;
headB = headB.next;
tmp = tmp.next;
}
}
if (headA != null) { //headBΪ�յ����
tmp.next = headA;
}
if(headB != null) { // headAΪ�յ����
tmp.next = headB;
}
return newHead.next;
}
//判断两个单链表是否有交点
public static ListNode getIntersectionNode
(ListNode headA, ListNode headB) {
if(headA == null || headB == null) {
return null;
}
ListNode pL = headA;//永远指向长的单链表
ListNode pS = headB;//永远指向短的单链表
int lenA = 0;
int lenB = 0;
//求的lenA lenB
while (pL != null) {
lenA++;
pL = pL.next;
}
while (pS != null) {
lenB++;
pS = pS.next;
}
//复位
pL = headA;
pS = headB;
//差值-》最长的单链表先走len步
int len = lenA-lenB;
if(len < 0) {
pL = headB;
pS = headA;
len = lenB-lenA;
}
//让pL先走len步
while (len > 0) {
pL = pL.next;
len--;
}
//开始一起走 (pL != pS ) {一人一步走}
while (pL != pS) {
pS = pS.next;
pL = pL.next;
}
if(pL == null) {
return null;
}
return pL;
}
//创建一个有交点的,单链表
public static void createCut
(ListNode headA, ListNode headB) {
headA.next = headB.next.next;
}
//复制带随机指针的链表 在OJ上做,OJ提供的ListNode有三个域
/**
public ListNode copyRandomList(Node head) {
if(head == null){
return null;
}
//1、老新进行进行交替链接
ListNode cur = head;
while(cur != null) {
ListNode node = new ListNode(cur.val,cur.next,null);
Node tmp = cur.next;
cur.next = node;
cur = tmp;
}
// 2、修改random
cur = head;
while(cur != null) {
if(cur.random != null) {
cur.next.random = cur.random.next;
cur = cur.next.next;
}else{
cur = cur.next.next;
}
}
//3、将老新节点 打开
cur = head;
ListNode newHead = cur.next;
while(cur.next != null) {
ListNode tmp = cur.next;
cur.next = tmp.next;
cur = tmp;
}
return newHead;
}
}
*/
public static void main(String[] args) {
MySingleList mySingleList = new MySingleList();
mySingleList.addIndex(0,199);
mySingleList.addLast(1);
mySingleList.addLast(2);
mySingleList.addLast(3);
mySingleList.addLast(4);
mySingleList.addLast(5);
mySingleList.display();
//逆置
System.out.println("逆置");
ListNode newhead = mySingleList.reverseList();
mySingleList.display2(newhead);
//返回中间节点
ListNode node = mySingleList.middleNode();
System.out.println(node.data);
//返回倒数第K个节点
ListNode node1 = mySingleList.findKthToTail(1);
System.out.println(node1.data);
//以x为基准划分区间
ListNode head = mySingleList.partition(3);
//划分区间后,有一个新的头,需要调用display2
mySingleList.display2(head);
//删除重复节点
ListNode newhead1 = mySingleList.deleteDuplication();
mySingleList.display2(newhead1);
//判断是否为回文结构
boolean flg = mySingleList.chkPalindrome();
System.out.println(flg);
//创造一个环
mySingleList.creteLoop();
//判断是否为环结构
boolean flg1 = mySingleList.hasCycle();
System.out.println(flg1);
//连接两个链表
MySingleList mySingleList1 = new MySingleList();
mySingleList1.addLast(3);
mySingleList1.addLast(3);
mySingleList1.addLast(4);
mySingleList1.addLast(5);
mySingleList1.addLast(6);
mySingleList1.addLast(7);
mySingleList1.display();
MySingleList mySingleList2 = new MySingleList();
mySingleList2.addLast(1);
mySingleList2.addLast(3);
mySingleList2.addLast(2);
mySingleList2.addLast(3);
mySingleList2.addLast(6);
mySingleList2.addLast(8);
mySingleList2.display();
ListNode newhead2 = mergeTwoLists(mySingleList1.head,mySingleList2.head);
mySingleList.display2(newhead2);
//判断连个链表是否相交
//创建一个有交点的单链表
createCut(mySingleList1.head,
mySingleList2.head);
ListNode node2 = getIntersectionNode(mySingleList1.head,
mySingleList2.head);
System.out.println(node2.data);
}
}