到底一个方法或者属性是否 需要添加static关键字的本质——取决于是否需要这类的所有对象共享这个变量。
static修饰类和不修饰类的区别? |
类的对象有自己的成员变量,但是没有人规定成员变量不能相同,所以对象A用可以有,对象B也 可以有,A用时干A的事情,B用时干B的事情,谁也不干预谁。如果这个类被static修饰了,等于说成员变量只有一份,A用完后B就发现没法用了,好比说A喜欢百度浏览器,他默认百度浏览器,把B喜欢的微软浏览器换了,B还会舒服得了?
所以说不用static修饰类时,每个对象的成员变量只和自己有关,别人不应该知道那么多。
单向链表 | 物理不连续的,当前每个结点只保存下一个方向的地址,只能一个方向遍历。 |
结点类的定义 | 每产生个结点就有个Node对象;Node类对象表示的是每个链表的结点; class Node{ int val //每个结点保存的值 Node next; //next属性叫保存下一个结点的地址 } 单链表的结点就是Node类的对象 |
这就是一个链表 | node1-->node2--->node3--->node4--->node5--->null 其中node5 next ==null |
用户知道的是头结点 | 通过头结点就能遍历出每个结点 |
单链表SingleLinkdeList类应该具备那些属性? | @1Node类头结点 @2int Size 存储结点个数,每个Node结点只有一个值。 如果我去坐火车,找到车次,再找车厢,就够了,因为我买的站票哦。 |
一个源文件中只能有一个public公共类,提供接口 类名不用public修饰:原文件中可以没有public 类,该类可以在同一个包内被访问。 private,私有的访问权限,也是最严格的访问权限,仅只能在设置了该权限的类中访问,利用这个访问权限,表现出封装思想。 | |
初始化 | |
用户调用的是单链表对象提供的增删改查方法就行,内部结点如何连接不关心,增删改查都在单链表类中定义。 | |
增加方法 | 每次增加一个数值,我都要加一个结点,那么我得new 一个Node 对象 |
头插 | 如果head!=null ,说明链表不是空的,我要插入一个newNode进去, |
先 newNode .next=head;//让newNode指向head结点 | |
后 head=newNode ;//newNode变成head结点。 | |
上面If语句有重复改成下面的简练写: 因为无论新的Node总是要变成头结点。 | |
如何遍历一个链表的每个结点? 从头结点开始,一次取出每个结点值,通过next引用走到下一个结点,直到next==null。 | |
可以用head吗? | 不可以,如果head==null,这个链表遍历一次就丢了! |
toString方法 | 两种都可以 |
|
package seqlist;public class SingleLinedList {所有的代码}public class LinedListTest {所有的输出检查}
根据索引插入数值 | ||
add( index,val) : 比如Add(2,10) 是要把索引为2位置的数值变成10,先要找到索引为2元素的前驱结点prev, : 先newNode.next=prev.next ; // 指向索引为2的老数值, : 后prev.neng=newNode ; //断掉前驱的旧指向,建立新的链接 为什么要讲究先后次序,想想怎么在火车跑动的时候加一节车厢在中间。 为什么要找前驱结点,因为单向链表只有一个方向。 | ||
增 | 如果在一个默认单链表中插入,只能插入index=0位置。 | 因为此时单链表为空,size=0,index大于0时非法。 AddIndex index illegal! 可先头插一下: 输出:2->10->Null |
输出:0->1->10->2->3->Null | ||
AddIndex(int index,int val)方法的代码: |
/** * 根据索引插入数值 * @param index * @param val */ public void AddIndex(int index,int val){ //若index索引非法,size是有效元素的个数 //当index=size时 相当于尾插,该方法应该允许尾插,合法。 if (index <0 || index >size){ System.err.println("AddIndex index illegal!"); return; } //插入任意位置要找到前驱结点,但是头结点没有前驱 //比如只有默认值null的情况时,index==0 if (index==0){ //调用头插 AddFirst(val); }else { Node newNode=new Node(); newNode.val=val; //当前索引合法且不是头插, // 定义一个前驱prev,找prev也是从head走,走0步是index=1的前驱, //走1步是Index为2的前驱,走2步是Index为3的前驱,所以我走1步就找到 //AddIndex(2,val)的前驱prev了。 //所以让prev遍历从index=0开始走index-1步 Node prev=head; //当index=size-1时,表示尾插的前驱索引,index再大就成Null的prev了。 //prev<index时表示index索引位的前驱,但是for i遍历时,每次i自动++; //所以i 要小于index-1 ,因为走完还有个i++,会多走一步的, for (int i = 0; i <index-1; i++) { prev=prev.next; } //走完遍历prev成了前驱 newNode.next=prev.next; prev.next=newNode; size++; } }
除了链表的头结点,都是 中间位置。只有头结点没前驱。
尾插代码 | |
检查 | |
输出 | 0->1->10->2->3->Null 0->1->10->2->3->5->Null |
查 找 | getByValue(int val) 查找第一个值为val的结点索引是多少,没有返回-1。 | |
验证 | | |
输出 | -1 | |
boolean contains(int val) 判断有没有值是val的元素 | | |
检查 | | |
输出 | false | |
boolean contains(int val) 简化写法 | | |
检查 | | |
输出 | true false | |
boolean contains(int val) 不借助方法: | 不验证了 | |
get(index){} 查找索引为index位置的结点值 | ||
index合法型方法: | alt+shift +enter 或者alt+enter 选择创建方法 | |
| ||
检查 | | |
输出 | 4->3->28->1->Null ------------ true false 1 28 |
附上代码 |
package seqlist;
/**
* int型单链表
*/
public class SingleLinedList {
//单链表初始化-->
private Node head;
//头结点叫head 默认为null
private int size ;
// 有效结点个数叫size 默认为0
/**
* 尾插
*/
public void AddLast(int val){
AddIndex(size,val);
}
/**
* 根据索引插入数值
* @param index
* @param val
*/
public void AddIndex(int index,int val){
//若index索引非法,size是有效元素的个数
//当index=size时 相当于尾插,该方法应该允许尾插,合法。
if (index <0 || index >size){
System.err.println("AddIndex index illegal!");
return;
}
//插入任意位置要找到前驱结点,但是头结点没有前驱
//比如只有默认值null的情况时,index==0
if (index==0){
//调用头插
AddFirst(val);
}else {
Node newNode=new Node();
newNode.val=val;
//当前索引合法且不是头插,
// 定义一个前驱prev,找prev也是从head走,走0步是index=1的前驱,
//走1步是Index为2的前驱,走2步是Index为3的前驱,所以我走1步就找到
//AddIndex(2,val)的前驱prev了。
//所以让prev遍历从index=0开始走index-1步
Node prev=head;
//当index=size-1时,表示尾插的前驱索引,index再大就成Null的prev了。
//prev<index时表示index索引位的前驱,但是for i遍历时,每次i自动++;
//所以i 要小于index-1 ,因为走完还有个i++,会多走一步的,
for (int i = 0; i <index-1; i++) {
prev=prev.next;
}
//走完遍历prev成了前驱
newNode.next=prev.next;
prev.next=newNode;
size++;
}
}
/**
* 头插法
* @param val
*/
public void AddFirst(int val){
// 增加方法叫Add
Node newNode=new Node();
//加上个新结点
newNode.val=val;
//存入数值val
if (head==null){
//head为null, newNode就是头结点
head=newNode;
}else {
//当前火车不为空
//新结点指向头
newNode.next=head;
//换新头结点
head=newNode;
}
// if (head!= null){
// newNode.next=head;
// }
// head=newNode;
size++;
}
public String toString(){
String ret="";
//把head赋值给x
// Node x=head;
// while ( x!= null){
// ret +=x.val;
// ret +="->";
// x=x.next;
// }
for (Node x=head ; x !=null ; x=x.next) {
ret +=x.val;
ret +="->";
}
ret += "Null";
return ret;
}
/**
* 查找第一个值为val的结点索引是多少
* @param val
* @return
*/
public int getByValue(int val){
int index=0;
for ( Node x=head; x !=null ; x=x.next) {
if (x.val==val){
return index;
}
index++;
}
return -1;
}
/**
* 判断有没有值是val的元素
* @param val
* @return
*/
public boolean contains(int val){
// int index =getByValue(val);
// if (index!=-1){
// return true;
// }
// return false;
// return getByValue(val)!=-1;
for (Node x = head; x!=null ; x=x.next) {
if (x.val==val){
return true;
}
}
return false;
}
/**
* 查找索引为index位置的数值
* @param index
* @return -1
*/
public int get(int index){
if (rangCheck(index)){
//双指针 i和x,通过i++使x走到i==index的位置,return x.val
Node x=head;
for (int i = 0; i < index; i++) {
x=x.next;
}
return x.val;
}
return -1;
}
/**
* 判断查询的合法性方法
* @param index
* @return
*/
private boolean rangCheck(int index) {
//在非插入的方法中不能取到size,size永远是当前有效元素的下一个索引
if (index <0||index >=size){
return false;
}
return true;
}
class Node{
//节点类初始化-->
int val; //每个结点保存的值 叫val
Node next; //当前结点的下一个结点地址 叫next
}
}