算法通关村第一关——链表青铜挑战笔记
前言
全文代码均以Java语言复现,主要为自己学习做记录,如有问题,还请各位大佬们指点指点。
单链表
组成
由众多的节点(node)构成,而每个节点都是由数据域(data)和指针域(next)组成,其中我们称第一个节点为头结点(head)!!!【这很重要】
public class Node{
private int data;
private Node next;
public Node(int data){
this.data=data;
}
public int getData(){
return data;
}
public Node getNext(){
return next;
}
public void setData(int data){
this.data=data;
}
public void setNext(Node next){
this.next=next;
}
}
单链表的基本操作
1. 构建和初始化
class SingleLinkedList{
// 申明头结点为全局变量
Node head;
public SingleLinkedList(){
head=null;
}
/**
* init & build 构建并初始化单链表
* @param a
*/
public void initSingleLinkedList(int a []){
Node node0 = new Node(a[0]);
Node node1 = new Node(a[1]);
Node node2 = new Node(a[2]);
Node node3 = new Node(a[3]);
Node node4 = new Node(a[4]);
// 头部节点依次相连
head=node0;
node0.next=node1;
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=null;
}
}
2. 获取单链表长度
/**
* 获取单链表长度
* @return
*/
public int getLengthSingleList(){
Node cur;
cur=head;
int len=0;
while(cur!=null){
len++;
cur=cur.next;
}
return len;
}
3. 遍历单链表
/**
* 遍历单链表
*/
public void display(){
Node cur=head;
while(cur!=null){
System.out.print(" "+cur.data);
cur=cur.next;
}
}
4. 插入节点
/**
* 插入节点
* @param singleLinkedList
* @param insertNode
* @param place
* @return
*/
public Node insertNode(SingleLinkedList singleLinkedList,Node insertNode, int place){
// 判斷插入鏈錶是否為空
if(head==null){
System.out.println("此鏈錶為空,不能插入");
return head;
}
// 判斷插入位置是否合法
int size=singleLinkedList.getLengthSingleList();
if(place>size+1||place<1){
System.out.println("位置参数越界");
return head;
}
// 表头位置插入节点
if(place==1){
insertNode.next=head;
// insertNode 作为表头
return insertNode;
}
// 指針指向頭結點
Node pNode=head;
// 取頭取尾 (舍零去長度減二)
// 当pNode走到前一个位置就弹出for循环 即i=place-1
for(int i =1;i<place-1;i++){
pNode=pNode.next;
}
// 插入节点的关键代码
insertNode.next=pNode.next;
pNode.next=insertNode;
return head;
}
ps:为什么先是insertNode.next=pNode.next 再是pNode.next=insertNode? 两者可以调换顺序吗?
答: 两者是不能调换顺序的!
我们可以这么理解,因为在遍历单链表的时候,是通过指针才能找到下一个节点。如果先是将当前的pNode.next指向insertNode的话,insertNode.next就不知道要指向哪里了,这样就导致单链表发生断裂,从而不无法进行遍历了。
如果是先要插入的insertNode.next指向pNode.next节点,然后再pNode.next=insertNode,这样单链表就不会出现断裂的情况了。
5. 删除节点
/**
* 删除节点
* @param singleLinkedList
* @param place
* @return
*/
public Node deleteNodeList(SingleLinkedList singleLinkedList,int place){
int size = singleLinkedList.getLengthSingleList();
if(place<1||place>size){
System.out.println("刪除位置不合法");
return head;
}
if(head==null){
System.out.println("鏈錶為空,無法刪除");
return null;
}
if(place==1){
return head.next;
}else{
Node pNode=head;
for(int i=1;i<place-1;i++){
pNode=pNode.next;
}
// 删除节点的关键代码
pNode.next=pNode.next.next;
}
return head;
}
}
双链表链表
构成
同样是节点构成,只不过是节点内部的结构发生了变化,多了一个前驱指针。
/**
* 双向链表的节点结构
*/
public class DoubleNode {
public DoubleNode prev;
public DoubleNode next;
public int data;
DoubleNode(int data){
this.data=data;
}
public DoubleNode getPrev() {
return prev;
}
public void setPrev(DoubleNode prev) {
this.prev = prev;
}
public DoubleNode getNext() {
return next;
}
public void setNext(DoubleNode next) {
this.next = next;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
双链表的基本操作
1. 构建和初始化
以first和last作为全局变量的节点,来构建双链表
class DoubleLinkedList{
// 定义头尾节点
DoubleNode first;
DoubleNode last;
// 构造函数 无节点 头尾指针均指向null
DoubleLinkedList (){
first=null;
last=first;
}
/**
* build & init
* @param a
* @return
*/
public void initDoubleLinkedList(int a[]){
DoubleNode doubleNode0 = new DoubleNode(a[0]);
DoubleNode doubleNode1= new DoubleNode(a[1]);
DoubleNode doubleNode2 = new DoubleNode(a[2]);
DoubleNode doubleNode3 = new DoubleNode(a[3]);
DoubleNode doubleNode4 = new DoubleNode(a[4]);
// 表頭(指針)
first=doubleNode0;
doubleNode0.next=doubleNode1;
doubleNode1.next=doubleNode2;
doubleNode2.next=doubleNode3;
doubleNode3.next=doubleNode4;
// 錶尾(指針)
last=doubleNode4;
doubleNode4.prev=doubleNode3;
doubleNode3.prev=doubleNode2;
doubleNode2.prev=doubleNode1;
doubleNode1.prev=doubleNode0;
// return last; 逆序 尾部開始
//return first;// 順序 頭部開始
}
}
2. 遍历双链表
/**
* 向前遍历
*/
public void displayForward(){
DoubleNode cur;
cur=first;
while(cur!=null){
System.out.print(" "+cur.getData());
cur=cur.getNext();
}
}
/**
* 向后遍历
*/
public void displayBackward(){
DoubleNode cur;
cur=last;
while(cur!=null){
System.out.print(" "+cur.getData());
cur=cur.getPrev();
}
}
3. 获取双链表长度
/**
* 获取双链表长度
* @return
*/
public int getLengthDoubleLinkedList(){
DoubleNode pNode;
pNode=first;
int len =0;
while(pNode!=null){
len++;
pNode=pNode.next;
}
return len;
}
4. 判空双链表
/**
* 判空双链表
* @return
*/
public boolean isEmpty(){
if(first==null){
return true;
}return false;
}
5. 插入节点
/**
* 头部插入
* @param data
*/
public void insertNodeFirst(int data){
DoubleNode newNode = new DoubleNode(data);
if(isEmpty()){
// 如果双链表为空
last=newNode;
}else{
// 如果不为空
first.prev=newNode;
}
newNode.next=first;
// 左移动头指针(first)
first=newNode;
}
/**
* 尾部插入
* @param data
*/
public void insertNodeLast(int data){
DoubleNode newNode = new DoubleNode(data);
if(isEmpty()){
// 如果双链表为空
first=newNode;
}else{
// 如果不为空 左移动头指针first
last.next=newNode;
}
newNode.prev=last;
// 右移动尾指针(last)
last=newNode;
}
/**
* 插入中间节点
* @param data
* @param pos
*/
public void insertNode(int data,int pos){
DoubleNode newNode = new DoubleNode(data);
DoubleNode cur=first;
int size=getLengthDoubleLinkedList();
if(pos<1||pos>size+1){
System.out.println("插入位置参数错误,无法插入");
}
for (int i = 1; i < pos-1; i++) {
cur=cur.next;
}
newNode.next=cur.next;
cur.next.prev=newNode;
cur.next=newNode;
newNode.prev=cur;
}
同理,在双链表中间插入节点的操作代码中,插入节点(newNode)的连接顺序是和单链表相似的,都是先右后左。
6. 删除节点
/**
* 删除头部节点
* @return
*/
public DoubleNode deleteHead(){
DoubleNode temp=first;
if(first.next==null){
last=null;
}else{
first.next.prev=null;
}
// 节点删除后 尾部指针需要右移动
first=first.next;
return temp;
}
/**
* 删除尾部节点
* @return
*/
public DoubleNode deleteLast(){
DoubleNode temp=last;
if(last.next==null){
last=null;
}else{
// 删除节点 也就是temp存储的内容
last.prev.next=null;
}
// 节点删除后 尾部指针需要左移动
last=last.prev;
return temp;
}
/**
* 删除节点
* @param pos
* @return
*/
public DoubleNode deleteNode(int pos){
DoubleNode cur = first;
DoubleNode temp;
if(isEmpty()){
System.out.println("链表为空,无法删除");
return null;
}else{
for (int i = 1; i < pos-1; i++) {
cur=cur.next;
}
temp=cur.next;
cur.next=temp.next;
temp.next.prev=cur;
}
return temp;
}