书接上文,继续编写链表的功能
4.链表的中间插入
在链表中,本身是没有下标这样的概念的,不像顺序表,顺序表根据下标访问元素,O(1)复杂度。链表需要遍历之后找到正确的位置才能进行插入,为O(N)复杂度。
但是这件事在JAVA中是个例外,JAVA标准库LinkedList引入了下标概念。

java
public int size(){
int size =0;
for(Node cur = head; cur.next!=null;cur = cur.next){
size++;
}
return size;
}
//4.中间插入的操作
public void add(int index,String value){
int size = size();
//1.首先判断index是否合法
if(index<0 || index >size){
throw new RuntimeException("下标超出范围");
}
//1.首先要找到index-1的prev节点
Node prev = head;
//上述代码出现了一个问题:如果是头插,那么就会导致循环无法进去,那么prev是第一个节点,插入的永远是下标为1的地方
//*特殊情况需要特殊考虑
if(index ==0){
addFirst(value);
return;
}
for (int i = 0; i < index-1; i++) {
prev = prev.next;
}
//3.此时进行修改操作
Node newNode = new Node(value);
newNode.next = prev. next;
prev.next = newNode;
}
//5.看某个元素是否被包含在链表当中
java
public boolean contains(String value){
for(Node cur = head;cur.next != null;cur = cur.next){
if(cur.value.equals(value)){
return true;
}
}
return false;
}
//6.找到了就返回index
java
public int indexOf(String value){
int index =0;
for(Node cur = head;cur.next != null;cur = cur.next){
if(cur.value.equals(value)){
return index;
}else {
index++;
}
}
return -1;
}
//7.根据下标删除

java
//7.根据下标删除
public void remove( int index){
int size = size();
//1.首先要判断index的值是否合法
if( index <0 || index >=size){
throw new RuntimeException("下标越界");
}
//*要考虑特殊的删除头节点
if(index ==0){
head = head.next;
return ;
}
//2.其次要找到上一个节点
Node prev = head;
for (int i = 0; i < index-1; i++) {
prev = prev.next;
}
//3.然后要进行删除操作
prev.next = prev.next.next;
}
//8.根据值来删除
java
public void remove(String value){
//还要考虑空链表的情况
if(head == null){
return;
}
//有关添加删除操作都要考虑前一个节点 所以每次创建的都是prev
Node prev = head;
for(;prev!=null;prev= prev.next){
if(prev.next!= null&&prev.next.equals(value)){
//找到了
break;
}
}
//出来之后有两种情况 一种是找到了value 另一种是遍历完了都没有找到value
if(prev == null){
return;
}else{
//找到了,进行删除操作
Node cur = prev.next;
prev.next = cur.next;
}
}
java
//9.clear()
public void clear(){
head = null;
}
至此,LinkedList基本写成了
总体:
java
package LinkedList;
//要想实现链表的基本功能,首先要表示链表的一个节点
//对于节点这个类来说,他的本身功能单一,比较简单
//如果高一些get set 方法 ,后续代码就会显得很难看
class Node{
public String value;
//节点保存的值
public Node next;
//这个节点的下一个元素
public Node(String value) {
this.value = value;
this.next = null;
}
//当我们创建一个Node的时候,就创建好了链表的头节点,此时链表头节点的值可以确定,且尚未含有下一个节点
}
//这是一个单链表的节点 双向链表还需要一个prev
public class MyLinkedList {
//把链表的头节点表示出来,此时整个链表就都能被获取到了
//此处不包含傀儡系欸但,head== null 的时候表示空的链表
private Node head = null;
//1.链表的头插操作
public void addFirst(String value){
Node newNode = new Node(value);
newNode.next =head;
head = newNode;
//head只是一个引用类型!!!
}
//2.遍历链表的操作
public String toString(){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
for (Node cur = head;cur!= null; cur= cur.next) {
stringBuilder.append(cur.value);
if(cur.next!= null) {
stringBuilder.append(",");
}
}
stringBuilder.append("]");
return stringBuilder.toString();
}
//3.尾插操作
public void addLast( String value){
//1.首先找到最后一个节点
Node tail = head;
//*还要考虑特殊的情况
if(head == null){
Node node = new Node(value);
head = node;
return;
}
for (;tail.next!=null; tail = tail.next) {
if(tail.next==null){
break;
}
}
//此时的tail也就是我们要找的最后一个节点
Node node = new Node(value);
tail.next = node;
node.next = null;
}
public int size(){
int size =0;
for(Node cur = head; cur.next!=null;cur = cur.next){
size++;
}
return size;
}
//4.中间插入的操作
public void add(int index,String value){
int size = size();
//1.首先判断index是否合法
if(index<0 || index >size){
throw new RuntimeException("下标超出范围");
}
//1.首先要找到index-1的prev节点
Node prev = head;
//上述代码出现了一个问题:如果是头插,那么就会导致循环无法进去,那么prev是第一个节点,插入的永远是下标为1的地方
//*特殊情况需要特殊考虑
if(index ==0){
addFirst(value);
return;
}
for (int i = 0; i < index-1; i++) {
prev = prev.next;
}
//3.此时进行修改操作
Node newNode = new Node(value);
newNode.next = prev. next;
prev.next = newNode;
}
//5.看某个元素是否被包含在链表当中
public boolean contains(String value){
for(Node cur = head;cur != null;cur = cur.next){
if(cur.value.equals(value)){
return true;
}
}
return false;
}
//6.找到了就返回index
public int indexOf(String value){
int index =0;
for(Node cur = head;cur != null;cur = cur.next){
if(cur.value.equals(value)){
return index;
}else {
index++;
}
}
return -1;
}
//7.根据下标删除
public void remove( int index){
int size = size();
//1.首先要判断index的值是否合法
if( index <0 || index >=size){
throw new RuntimeException("下标越界");
}
//*要考虑特殊的删除头节点
if(index ==0){
head = head.next;
return ;
}
//2.其次要找到上一个节点
Node prev = head;
for (int i = 0; i < index-1; i++) {
prev = prev.next;
}
//3.然后要进行删除操作
prev.next = prev.next.next;
}
//8.根据值来删除
public void remove(String value){
//还要考虑空链表的情况
if(head == null){
return;
}
//有关添加删除操作都要考虑前一个节点 所以每次创建的都是prev
Node prev = head;
for(;prev!=null;prev= prev.next){
if(prev.next!= null&&prev.next.equals(value)){
//找到了
break;
}
}
//出来之后有两种情况 一种是找到了value 另一种是遍历完了都没有找到value
if(prev == null){
return;
}else{
//找到了,进行删除操作
Node cur = prev.next;
prev.next = cur.next;
}
}
//9.clear()
public void clear(){
head = null;
}
}
java
class Solution {
public ListNode removeElements(ListNode head, int val) {
//1.首先判断链表是否为空
if(head == null){
return null;
}
//2.利用循环删除每一个值为val的元素,但是是从head后面开始删除的!
ListNode prev = head;
ListNode cur = prev.next;
while(cur!=null){
if(cur.val == val){
//就触发删除操作
prev.next = cur.next;
//还要将cur进行后置
cur = prev.next;
}else{
prev = cur;
cur =cur.next;
}
}
//cur == null 再判定开头的元素
if(head.val==val){
head = head.next;
}
return head;
}
}

1.这道题的要点就在于头节点的删除,如果head=[7,7,7,7],我们就先不管头节点,先把后面值等于val的节点删除,然后循环出来再去考虑头节点。


现在要得到这个链表的翻转链表
思路:分别设置三个引用变量:prev,cur,next。使三者遍历整个链表,按照如下图所示的操作完成链表的翻转。

*为什么一定要设置三个,而不能只使用cur?
因为在完成翻转操作之后,我们还想让循环继续,但是此时cur.next=prev,所以我们要通过next这个引用变量为我们指明前方的道路
也就是说,pev=cur;cur = next; next = next.next;
一直到cur == null,然后跳出循环
java
class Solution {
public ListNode reverseList(ListNode head) {
//1.首先判断链表是否为空
if(head == null){
return null;
}
//2.如果链表里只含有一个元素,那么翻转还是不反转没有任何影响
if(head.next == null){
return head;
}
//3.来处理一般情况
//首先要创建三个引用变量
ListNode prev = null;
ListNode cur = head;
ListNode next = cur.next;
//再创建一个新的头节点,待会儿返回
ListNode newHead = null;
while(cur!=null){
next = cur.next;
if(next == null){
//已经全部完成了
newHead = cur;
//这个地方不能break,因为下面的操作还需要更新!!!
}
cur.next = prev;
prev = cur;
cur = next;
}

思路:首先我们可以计算处链表的长度,其次只要将链表的长度/2 之后再遍历我们就可以得到第二个中间节点的值了
java
class Solution {
public ListNode middleNode(ListNode head) {
int size = 0;
for(ListNode cur = head ; cur!= null;cur = cur.next){
size++;
}
int num = size/2;
ListNode cur = head;
while(num != 0){
num--;
cur = cur.next;
}
return cur;
}
}

思路:1.创建新的链表表示最终结构
2.搞两个引用,分别指向两个链表
3.比较这两个引用的值,谁小,就把哪个节点取出来,插入到新链表的末尾,如果这两个引用,有任何一个指向了null,说明该链表就结束了,就把另一个链表剩余的元素都添加到新链表的末尾即可。
代码:
java
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
//1.判断list1为空链表的情况
if(list1 == null){
return list2;
}
//2。判断list2为空链表的情况
if(list2 == null){
return list1;
}
//3.考虑一般情况
//为了使后续的插入方便我们首先创建一个傀儡节点以及末尾节点
ListNode newHead = new ListNode(0);
ListNode tail = newHead;
ListNode cur1 = list1;
ListNode cur2 = list2;
while(cur1!=null && cur2 !=null){
if(cur1.val < cur2.val){
//cur1比较小 所以把cur1放进来
ListNode newNode =cur1;
tail.next = newNode;
tail = newNode;
cur1 = cur1.next;
}else{
ListNode newNode =cur2;
tail.next = newNode;
tail = newNode;
cur2 = cur2.next;
}
}
//出来的时候,要么cur1还有剩余,要么cur2还有剩余
if(cur1 != null){
//把剩余的链表给他接上去
tail.next = cur1;
}
if(cur2 != null){
tail.next = cur2;
}
return newHead.next;//不能返回newHead 要返回傀儡节点的下面一个节点
}
}
写不动了!明天再见!