目录
一、链表的创建及基本增删改查操作
1.IList接口
public interface IList {
//头插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data);
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key);
//删除第一次出现关键字为key的节点
public void remove(int key);
//删除所有值为key的节点
public void removeAllKey(int key);
//得到单链表的长度
public int size();
public void display();
public void clear();
}
2.MySingleLinkedList类
public class MySingleLinkedList implements IList {
static class ListNode{
public int val;
public ListNode next;
public ListNode(int val){
this.val = val;
}
}
//定义全局变量首节点
public ListNode head;
public void createList(){
//创建5个节点
ListNode node1 = new ListNode(12);
ListNode node2 = new ListNode(23);
ListNode node3 = new ListNode(34);
ListNode node4 = new ListNode(45);
ListNode node5 = new ListNode(56);
//连接5个节点,直接存入node对象,其实存入的就是地址
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = null;
//局部变量node1会在函数结束以后销毁,所以将其赋给全局变量head
this.head = node1;
}
@Override
public void addFirst(int data) {
ListNode firstNode = new ListNode(data);
if (this.head == null){
this.head = firstNode;
}else {
firstNode.next = this.head;
this.head = firstNode;
}
}
@Override
public void addLast(int data) {
ListNode lastNode = new ListNode(data);
ListNode currNode = this.head;
if (this.head == null) {
this.head = lastNode;
} else {
//此处可理解为不进入currNode,所以打印不出来它的val
//这就和display中currNode!=null形成了对比,
//因为display中需要进入到currNode去找到val值
while (currNode.next != null) {
currNode = currNode.next;
}
currNode.next = lastNode;
}
}
@Override
public void addIndex(int index, int data) {
if (index<0 || index > size()){
System.out.println("插入位置不合理");
return ;
}
if (index == 0){
addFirst(data);
return ;
}
if (index == size()){
addLast(data);
return;
}
ListNode dataNode = new ListNode(data);
ListNode currNode = this.head;
int count = 0;
while (count < index-1){
count++;
currNode = currNode.next;
}
dataNode.next = currNode.next;
currNode.next = dataNode;
}
@Override
public boolean contains(int key) {
ListNode cur = head;
while (cur!=null){
if (cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
@Override
public void remove(int key) {
if (this.head == null){
System.out.println("链表无节点");
return ;
}
if (this.head.val == key){
this.head = this.head.next;
return;
}
ListNode currNode = this.head;
while (currNode.next != null) {
if (currNode.next.val == key) {
currNode.next = currNode.next.next;
break;
} else {
currNode = currNode.next;
}
}
if (currNode.next == null){
System.out.println("没有你要删除的数");
}
}
@Override
public void removeAllKey(int key) {
if(head == null){
return ;
}
ListNode prev = head;
ListNode cur = head.next;
while (cur!=null){
if (cur.val == key){
prev.next = cur.next;
cur = cur.next;
}else {
prev = cur;
cur = cur.next;
}
}
if(head.val == key){
head = head.next;
}
}
@Override
public int size() {
int count = 0;
ListNode currNode = this.head;
while (currNode != null){
count++;
currNode = currNode.next;
}
return count;
}
@Override
public void display() {
ListNode currNode = this.head;
while (currNode != null){
System.out.print(currNode.val + " ");
currNode = currNode.next;
}
System.out.println();
}
@Override
public void clear() {
ListNode cur = head;
while (cur!=null){
ListNode curNext = cur.next;
cur.next = null;
cur = cur.next;
}
head = null;
}
}
}
3.Test测试类
public class Test {
public static void main(String[] args) {
MySingleLinkedList mySingleLinkedList1 = new MySingleLinkedList();
mySingleLinkedList1.createList();
mySingleLinkedList1.addFirst(6);
mySingleLinkedList1.addFirst(7);
// System.out.println(mySingleLinkedList1.contains(7));
mySingleLinkedList1.display();
MySingleLinkedList mySingleLinkedList2 = new MySingleLinkedList();
mySingleLinkedList2.createList();
mySingleLinkedList2.addFirst(7);
mySingleLinkedList2.addFirst(8);
mySingleLinkedList2.display();
mergeTwoLists(mySingleLinkedList1.head,mySingleLinkedList2.head);
// System.out.println("size:" + mySingleLinkedList.size());
// System.out.println("==================");
//
// mySingleLinkedList.addLast(999);
// mySingleLinkedList.display();
//
// System.out.println("==================");
// mySingleLinkedList.addIndex(1,123);
// mySingleLinkedList.addIndex(9,5876);
// mySingleLinkedList.display();
//
// System.out.println("===============");
mySingleLinkedList.remove(123);
mySingleLinkedList.remove(1234);
// mySingleLinkedList.remove(5876);
// mySingleLinkedList.display();
// System.out.println("test====================");
// mySingleLinkedList.display();
// mySingleLinkedList.reverseList();
// mySingleLinkedList.display();
// System.out.println("mid==============");
// mySingleLinkedList.middleNode1();
// mySingleLinkedList.display();
// mySingleLinkedList.middleNode2();
// mySingleLinkedList.display();
// System.out.println("kkk");
// mySingleLinkedList.FindKthToTail(2);
// mySingleLinkedList.display();
}
public static MySingleLinkedList.ListNode mergeTwoLists(MySingleLinkedList.ListNode head1,
MySingleLinkedList.ListNode head2) {
MySingleLinkedList.ListNode newH = new MySingleLinkedList.ListNode(-1);
MySingleLinkedList.ListNode tmpH = newH;
while (head1 != null && head2 != null) {
if(head1.val < head2.val) {
tmpH.next = head1;
head1 = head1.next;
tmpH = tmpH.next;
}else {
tmpH.next = head2;
head2 = head2.next;
tmpH = tmpH.next;
}
}
if (head1 != null) {
tmpH.next = head1;
}
if (head2 != null) {
tmpH.next = head2;
}
return newH.next;
}
}
二、删除链表中给定值val的所有节点
正常遍历+比值即可
public void removeAllKey(int key) {
if(head == null){
return ;
}
ListNode prev = head;
ListNode cur = head.next;
while (cur!=null){
if (cur.val == key){
prev.next = cur.next;
cur = cur.next;
}else {
prev = cur;
cur = cur.next;
}
}
if(head.val == key){
head = head.next;
}
}
三、反转一个链表
思路:每次都将head后的节点,头插到head前面。
技巧:每次都要先将cur.next的值赋给一个变量保存起来,再将cur拿去偷插。第二个节点cur = head.next,第三个节点currNext = cur.next。
public ListNode reverseList(){
if (head==null){
return head;
}
if (head.next == null){
return head;
}
ListNode cur = head.next;
head.next = null;
while (cur!=null){
ListNode currNext = cur.next;
cur.next = head;
head = cur;
cur = currNext;
}
return head;
}
四、返回中间节点
思路1:中间节点位置为总长len/2。先遍历一遍获取len,再遍历一边获得mid。
public ListNode middleNode1(){
int ListCount = 0; //列表长度
ListNode curr = head;
while (curr != null){
ListCount ++;
curr = curr.next;
}
int count2 = 0;
ListNode curr2 = head;
while (count2 < ListCount/2){
count2 ++;
curr2 = curr2.next;
}
this.head = curr2;
return this.head;
}
思路2:双指针模型,让fast和slow都从head出发,fast一次走两步,slow一次走一步。走完以后slow的节点就是中间节点。(双指针的思路,很多算法题都可用)
public ListNode middleNode2() {
ListNode fast = head;
ListNode slow = head;
while (fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
}
this.head = slow;
return this.head;
}
五、合并两个有序链表
思路:创建一个起始虚拟节点newT,将其赋值给tempT。head1、head2分别指向两条链表。对比head1和head2指向的值,谁小把谁加到tempT后面。tempT需要更新,永远指向最后一步操作所指向的节点。
public static MySingleLinkedList.ListNode mergeTwoLists(
MySingleLinkedList.ListNode head1,
MySingleLinkedList.ListNode head2) {
MySingleLinkedList.ListNode newH = new MySingleLinkedList.ListNode(-1);
MySingleLinkedList.ListNode tmpH = newH;
while (head1 != null && head2 != null) {
if(head1.val < head2.val) {
tmpH.next = head1;
head1 = head1.next;
tmpH = tmpH.next;
}else {
tmpH.next = head2;
head2 = head2.next;
tmpH = tmpH.next;
}
}
if (head1 != null) {
tmpH.next = head1;
}
if (head2 != null) {
tmpH.next = head2;
}
return newH.next;
}
六、链表的回文结构
思路:将中间节点到末尾节点的Node反转。1.找到中间位置;2.反转后面的节点;3.从前到后,从后往前,对比。
public boolean chkPalindrome() {
if (head==null){
return true;
}
//1.找到中间位置
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
}
//2.翻转
ListNode cur = slow.next;
while(cur!=null){
ListNode curNext = cur.next;
cur.next = slow;
cur = cur.next;
slow = cur;
cur = curNext;
}
//3.从前到后,从后到前判断
while (head!=slow){
if (head.val != slow.val){
return false;
}
if (head.next == slow){
return true;
}
head = head.next;
slow = slow.next;
}
return true;
}
七、输出链表倒数第k个节点
思路:双指针思路,先让fast走k-1步,然后fast和slow一起走,直到fast走完,此时slow的位置即为倒数第k个节点。
public ListNode FindKthToTail(int k) {
//判断k的合法性
if (k<=0 || head==null){
return null;
}
ListNode fast = head;
ListNode slow = head;
for (int i = 0; i < k-1; i++) {
fast = fast.next;
//处理k太大问题
if (fast == null){
return null;
}
}
while (fast.next!=null){
fast = fast.next;
slow = slow.next;
}
head = slow;
return slow;
}
八、输入值x,小于在前,大于在后
思路:遍历原链表,将小于的用尾插法存入一个bs、be链表,大于的存入as、ae链表,最后连接起来
public ListNode div(int k){
if (head==null){
return null;
}
ListNode bs = null;
ListNode be = null;
ListNode as = null;
ListNode ae = null;
ListNode curr = head;
//遍历每个节点
while (curr!=null){
//比k小
if (curr.val < k){
//如果是第一次插入
if (bs == null){
//此时插入第一个节点
bs = curr;
be = curr;
}else {
//be永远指向最后一个节点
be.next = curr;
be = be.next;
}
}else {
if (as == null){
//此时插入第一个节点
as = curr;
ae = curr;
}else {
//ae永远指向最后一个节点
ae.next = curr;
ae = ae.next;
}
}
curr = curr.next;
}
//开始将小于串、k、大于串,串起来
if (bs == null){
return as;
}
be.next = as;
if (as != null){
//说明第二个区间有数据,需要将第二个区间最后一个元素next设为null
ae.next = null;
}
head = bs;
return head;
}
九、输入两个链表,找出它们的第一个公共结点
思路:计算两个链表的长度差k,让长的链表先走k步,然后两个链表一起走,相同点就是公共节点。
//找出第一个相交节点
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int count1 = 0;
int count2 = 0;
ListNode curr1 = headA;
ListNode curr2 = headB;
//计算两个链表的长度
while (curr1!=null){
curr1 = curr1.next;
count1 ++;
}
while (curr2!=null){
curr2 = curr2.next;
count2 ++;
}
//计算二者差值
int gap = Math.abs(count1-count2);
//如果head1长
curr1 = headA;
curr2 = headB;
if (count1>count2){
//先让curr1走gap步
for (int i = 0; i < gap; i++) {
curr1 = curr1.next;
}
//再一起走
while (curr1!=curr2){
curr1 = curr1.next;
curr2 = curr2.next;
}
return curr1;
}else {
//先让curr2走gap步
for (int i = 0; i < gap; i++) {
curr2 = curr2.next;
}
//再一起走
while (curr1!=curr2){
curr2 = curr2.next;
curr1 = curr1.next;
}
return curr2;
}
}
十、给定一个链表,判断链表中是否有环
思路:让fast一次走2步,slow一次走1步,如果有第一次相同的地方就代表有环。
//判断是否有环
public boolean hasCycle() {
ListNode fast = head;
ListNode slow = head;
//循环判断条件为fast永远走不到头。表示有环就一直在循环里走。
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) return true;
}
return false;
}
十一、给定一个链表,返回链表开始入环的第一个节点。
思路:1.找到相遇的地点;2一个从相遇点出发,一个从起点出发,再次相遇点即为入口。
public ListNode detectCycle() {
ListNode fast = head;
ListNode slow = head;
//循环判断条件为fast永远走不到头。表示有环就一直在循环里走。
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
//如果相遇,就break
if (fast == slow){
break;
}
}
if (fast==null || fast.next == null){
return null;
}
//如果有环,程序走到这里,fast指向相遇点,让slow回起点
//一人一步走
slow = head;
while (slow!=fast){
fast = fast.next;
slow = slow.next;
}
//相遇点即为入口点
return slow;
}