链表
链表是有序列表,线性结构。 链表是以节点的方式来存储,是链式存储。 每个节点包含data域,next域(指向下一个节点)。 节点之间不一定是连续的地址,链表存储空间不连续(不像数组)。 链表分带头节点的链表和没有带头节点的链表,根据实际的需求来确定。
单链表的增删改查
添加节点(创建)
先创建一个hear头节点(头结点不存放数据,仅仅作为当前链表的入口;head 字段的值不能改变,一旦改变,就丢失了整个链表的入口,我们也就无法通过 head 找到链表了;头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义[当然有些情况下也可存放链表的长度、用做监视哨等等]。),作用就是表示单链表的头。 后面每添加一个节点,就直接加入到链表的最后(首先需要遍历链表,找到链表最后一个节点,当 temp.next == null时,temp 节点指向链表最后一个节点)。
添加【添加时根据编号排序】节点(创建)
首先找到新添加的节点的位置,是通过辅助变量,通过遍历来搞定。 新的节点.next=temp.next。 将temp.next=新的节点。 首先需要遍历链表,找到链表中编号值比 node.no 大的节点,暂且叫它 biggerNode ,然后把 node 插入到 biggerNode 之前即可。 怎么找 biggerNode ?当 temp.next.no > node.no 时,这时temp.next 节点就是 biggerNode 节点。 为什么是 temp.next 节点?只有找到 temp 节点和 temp.next(biggerNode )节点,才能在 temp 节点和 temp.next 节点之间插入 node 节点
遍历链表
通过一个辅助变量遍历,帮助遍历整个链表。 何时遍历完成?temp == null 表明当前节点为 null ,即表示已到链表末尾。 如何遍历?temp = temp.next ,每次输出当前节点信息之后,temp 指针后移。
修改节点
先找到要修改的该节点temp,通过遍历(如何找到指定节点?temp.no = newNode.no)。 找到后,temp.name=newNode.name。
删除节点
找到需要删除的这个节点的前一个节点temp。 temp.next=temp.next.next。 被删除的节点,将不会有其他引用指向,会被垃圾回收机制回收。
获取倒数第几的节点
首先获取有效节点总长度-指定倒数第几=N. 然后遍历到 N次后返回
单链表的反转(实际节点调换)
定义新链表 DataNode reverseHead = new DataNode();。 遍历原链表, 每遍历一个节点, 将其取出, 并加到 reverseHead的最前端。
单链表的反转(只是反转输出, 实际节点未反转)
通过栈数据结构, 利用栈的先进后出的特点, 实现逆序输出的效果。
代码示例 1 - 添加、遍历
class DataNode {
public int no;
public String title;
public DataNode next;
public DataNode ( ) { }
public DataNode ( int no, String title) {
this . no = no;
this . title = title;
}
@Override
public String toString ( ) {
return "DataNode [no=" + no + ", title=" + title + "]" ;
}
}
class SingleLinkedList {
private DataNode head = new DataNode ( ) ;
public DataNode getHead ( ) {
return head;
}
public void add ( DataNode DataNode) {
DataNode temp = head;
while ( true ) {
if ( temp. next == null) {
break ;
}
temp = temp. next;
}
temp. next = DataNode;
}
public void print ( ) {
if ( head. next == null) {
System. out. println ( "链表为空!" ) ;
return ;
}
DataNode temp = head. next;
while ( true ) {
if ( temp == null) {
break ;
}
System. out. println ( temp) ;
temp = temp. next;
}
}
}
public class SingleLinkedListApp {
public static void main ( String[ ] args) {
SingleLinkedList list = new SingleLinkedList ( ) ;
list. add ( new DataNode ( 4 , "节点4" ) ) ;
list. add ( new DataNode ( 2 , "节点2" ) ) ;
list. add ( new DataNode ( 1 , "节点1" ) ) ;
list. add ( new DataNode ( 3 , "节点3" ) ) ;
list. print ( ) ;
}
}
输出:
> DataNode [ no= 4 , title= 节点4 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 3 , title= 节点3 ]
代码示例 2 - 排序添加(添加时根据编号排序)
public void addByOrder ( DataNode DataNode) {
DataNode temp = head;
boolean bRepeat = false ;
while ( true ) {
if ( temp. next == null) {
break ;
}
if ( temp. next. no > DataNode. no) {
break ;
} else if ( temp. next. no == DataNode. no) {
bRepeat = true ;
break ;
}
temp = temp. next;
}
if ( bRepeat) {
System. out. printf ( "重复节点 %d, 不能添加\n" , DataNode. no) ;
} else {
DataNode. next = temp. next;
temp. next = DataNode;
}
}
SingleLinkedList list = new SingleLinkedList ( ) ;
list. addByOrder ( new DataNode ( 4 , "节点4" ) ) ;
list. addByOrder ( new DataNode ( 2 , "节点2" ) ) ;
list. addByOrder ( new DataNode ( 1 , "节点1" ) ) ;
list. addByOrder ( new DataNode ( 3 , "节点3" ) ) ;
list. print ( ) ;
输出:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
代码示例 3 - 修改
public void update ( DataNode newDataNode) {
if ( head. next == null) {
System. out. println ( "链表为空!" ) ;
return ;
}
DataNode temp = head. next;
boolean flag = false ;
while ( true ) {
if ( temp == null) {
break ;
}
if ( temp. no == newDataNode. no) {
flag = true ;
break ;
}
temp = temp. next;
}
if ( flag) {
temp. title = newDataNode. title;
} else {
System. out. printf ( "没有找到 编号 %d 的节点\n" , newDataNode. no) ;
}
}
SingleLinkedList list = new SingleLinkedList ( ) ;
list. addByOrder ( new DataNode ( 4 , "节点4" ) ) ;
list. addByOrder ( new DataNode ( 2 , "节点2" ) ) ;
list. addByOrder ( new DataNode ( 1 , "节点1" ) ) ;
list. addByOrder ( new DataNode ( 3 , "节点3" ) ) ;
System. out. println ( "原链表:" ) ;
list. print ( ) ;
DataNode newStu = new DataNode ( 3 , "节点03" ) ;
list. update ( newStu) ;
System. out. println ( "修改后的链表:" ) ;
list. print ( ) ;
输出:
> 原链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
> 修改后的链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点03 ]
> DataNode [ no= 4 , title= 节点4 ]
代码示例 4 - 删除
public void del ( int no) {
DataNode temp = head;
boolean flag = false ;
while ( true ) {
if ( temp. next == null) {
break ;
}
if ( temp. next. no == no) {
flag= true ;
break ;
}
temp = temp. next;
}
if ( flag) {
temp. next = temp. next. next;
} else {
System. out. printf ( "要删除的编号 %d 节点不存在" , no) ;
}
}
SingleLinkedList list = new SingleLinkedList ( ) ;
list. addByOrder ( new DataNode ( 4 , "节点4" ) ) ;
list. addByOrder ( new DataNode ( 2 , "节点2" ) ) ;
list. addByOrder ( new DataNode ( 1 , "节点1" ) ) ;
list. addByOrder ( new DataNode ( 3 , "节点3" ) ) ;
System. out. println ( "原链表:" ) ;
list. print ( ) ;
list. del ( 2 ) ;
System. out. println ( "删除后的链表:" ) ;
list. print ( ) ;
输出:
> 原链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
> 删除后的链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
代码示例 5 - 链表长度(有效节点的个数, 不统计头节点)
public int getLength ( ) {
if ( head. next == null) {
return 0 ;
}
int length = 0 ;
DataNode temp = head. next;
while ( temp != null) {
length++ ;
temp = temp. next;
}
return length;
}
SingleLinkedList list = new SingleLinkedList ( ) ;
list. addByOrder ( new DataNode ( 4 , "节点4" ) ) ;
list. addByOrder ( new DataNode ( 2 , "节点2" ) ) ;
list. addByOrder ( new DataNode ( 1 , "节点1" ) ) ;
list. addByOrder ( new DataNode ( 3 , "节点3" ) ) ;
System. out. println ( "原链表:" ) ;
list. print ( ) ;
System. out. println ( "有效节点个数: " + list. getLength ( ) ) ;
输出:
> 原链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
> 有效节点个数: 4
代码示例 6 - 获取倒数第几的节点
public DataNode findLastIndexNode ( int index) {
if ( head. next == null) {
return null;
}
int size = getLength ( ) ;
if ( index <= 0 || index > size) {
return null;
}
DataNode temp = head. next;
int leftSize = size - index;
for ( int i = 0 ; i< leftSize; i++ ) {
temp = temp. next;
}
return temp;
}
SingleLinkedList list = new SingleLinkedList ( ) ;
list. addByOrder ( new DataNode ( 4 , "节点4" ) ) ;
list. addByOrder ( new DataNode ( 2 , "节点2" ) ) ;
list. addByOrder ( new DataNode ( 1 , "节点1" ) ) ;
list. addByOrder ( new DataNode ( 3 , "节点3" ) ) ;
System. out. println ( "原链表:" ) ;
list. print ( ) ;
int lastNo = 2 ;
DataNode DataNode = list. findLastIndexNode ( lastNo) ;
System. out. printf ( "倒数第 %d 个节点: " , lastNo) ;
System. out. println ( DataNode) ;
输出:
> 原链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
> 倒数第 2 个节点: DataNode [ no= 3 , title= 节点3 ]
代码示例 7 - 单链表的反转(实际节点调换)
public void reversetList ( ) {
if ( head. next == null || head. next. next == null) {
return ;
}
DataNode cur = head. next;
DataNode next;
DataNode reverseHead = new DataNode ( ) ;
while ( cur != null) {
next = cur. next;
cur. next = reverseHead. next;
reverseHead. next = cur;
cur = next;
}
head. next = reverseHead. next;
}
SingleLinkedList list = new SingleLinkedList ( ) ;
list. addByOrder ( new DataNode ( 4 , "节点4" ) ) ;
list. addByOrder ( new DataNode ( 2 , "节点2" ) ) ;
list. addByOrder ( new DataNode ( 1 , "节点1" ) ) ;
list. addByOrder ( new DataNode ( 3 , "节点3" ) ) ;
System. out. println ( "原链表:" ) ;
list. print ( ) ;
System. out. println ( "反转链表:" ) ;
list. reversetList ( ) ;
list. print ( ) ;
输出:
> 原链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
> 反转链表:
> DataNode [ no= 4 , title= 节点4 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 1 , title= 节点1 ]
代码示例 8 - 单链表的反转(只是反转输出, 实际节点未反转)
public void reversePrint ( ) {
if ( head. next == null) {
return ;
}
Stack< DataNode> stack = new Stack < > ( ) ;
DataNode cur = head. next;
while ( cur != null) {
stack. push ( cur) ;
cur = cur. next;
}
while ( stack. size ( ) > 0 ) {
System. out. println ( stack. pop ( ) ) ;
}
}
SingleLinkedList list = new SingleLinkedList ( ) ;
list. addByOrder ( new DataNode ( 4 , "节点4" ) ) ;
list. addByOrder ( new DataNode ( 2 , "节点2" ) ) ;
list. addByOrder ( new DataNode ( 1 , "节点1" ) ) ;
list. addByOrder ( new DataNode ( 3 , "节点3" ) ) ;
System. out. println ( "原链表:" ) ;
list. print ( ) ;
System. out. println ( "逆序打印单链表:" ) ;
list. reversePrint ( ) ;
输出:
> 原链表:
> DataNode [ no= 1 , title= 节点1 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 4 , title= 节点4 ]
> 逆序打印单链表:
> DataNode [ no= 4 , title= 节点4 ]
> DataNode [ no= 3 , title= 节点3 ]
> DataNode [ no= 2 , title= 节点2 ]
> DataNode [ no= 1 , title= 节点1 ]