package linklist;
import org.junit.Test;
import java.util.Hashtable;
/**
* 明白头指针与头结点的区别:
* 头结点,放在第一个元素结点之前,其数据域一般无意义(存放链表长度)
* 头指针:链表中指向第一个结点的指针
* @author kankan
* @creater 2019-06-01 7:56
*/
public class LinkedList {
int size;//链表节点的个数
Node head = null;//链表头的引用
/**
* 头插法
*/
public int addNodeHead(int data){
Node newNode = new Node(data);//1.生成新结点
if (0 == size){//2.判断是否是头结点
head = newNode;
}else{
newNode.next = head;
head = newNode;//让头指针指向第一个结点
}
size++;
return data;
}
// 注意这里 index 从 0 开始
public int addNodeIndex(int index,int data) {
if (index < 0 || index > size) {
System.out.println("角标越界!");
return -1;
}
if (index == 0) {
return addNodeHead(data);//头插法
} else if (index == size) {
return addNodeTail(data);//尾插法
} else {
Node pre = head;
Node cur = head.next;
//
while (pre != null && index > 1) {
pre = pre.next;
cur = cur.next;
index--;
}
//循环结束后 pre 保存的是索引的上一个节点 而 cur 保存的是索引值当前的节点
Node node = new Node(data);
node.next = cur;
pre.next = node;
size++;
}
return data;
}
/**
* 向链表中插入数据
* 尾插法
* 每次的新结点都插在终端结点的后面
*/
public int addNodeTail(int data){
Node newNode = new Node(data);//1.生成新结点
if (0 == size){//2.判断头指针指向第一个是否为null
head = newNode;
}else{
//3.遍历到终点
Node tmp = head;
while (tmp.next != null){
tmp = tmp.next;
}
tmp.next = newNode;//4.遍历到终端结点,进行插入
}
size++;
return data;
}
//在链表头删除元素
public boolean deleteHead(){
if (null == head) {
System.out.println("当前链表为空!");
return false;
}
int obj = head.data;
head = head.next;
size--;
return true;
}
/**
* 删除第index个结点
* 第一个结点的存储位置称为头指针
*/
public boolean deleteNode(int index){
if (index < 0 || index >= size){
System.out.println("下标越界或小于0");
return false;
}
//删除链表第一个元素
if (0 == index){
head = head.next;
size--;
return true;
}
int i = 1;
Node pre= head;
Node cur = pre.next;
while (cur != null){
if (i == index){
pre.next = cur.next;
size--;
return true;
}
pre = cur;
cur = cur.next;
i++;
}
return true;
}
//尾删除法
public boolean deleteTail(){
if (null == head) {
System.out.println("当前链表为空!");
return false;
}
if (1 == size){
head = null;
}else{
Node deleteNode = head;
//找到倒数第二个不指向null结点,因为我们删除倒数第一个结点
while (deleteNode.next != null && deleteNode.next.next != null) {
deleteNode = deleteNode.next;
}
deleteNode.next = null;//倒数第二个结点的next赋null
}
size--;
return true;
}
//显示节点信息
public void display(){
if(size > 0){
Node node = head;
int tempSize = size;
if(tempSize == 1){//当前链表只有一个节点
System.out.println("["+node.data+"]");
return;
}
while(tempSize>0){
if(node.equals(head)){
System.out.print("["+node.data+"->");
}else if(node.next == null){
System.out.print(node.data+"]");
}else{
System.out.print(node.data+"->");
}
node = node.next;
tempSize--;
}
System.out.println();
}else{//如果链表一个节点都没有,直接打印[]
System.out.println("[]");
}
}
/**
* 选择排序
* 返回排序后的头结点
* @param args
*/
public Node orderList(){
Node nextNode = null;
int temp = 0;
Node curNode = head;
while(curNode.next != null){
nextNode = curNode.next;
while (nextNode != null){
//数据从小到大
if (curNode.data > nextNode.data){
temp = curNode.data;
curNode.data = nextNode.data;
nextNode.data = temp;
}
nextNode = nextNode.next;
}
curNode = curNode.next;
}
return head;
}
//从链表中删除重复数据
/**
* 遍历链表,将链表中的数据存储到HashTable中,若当前遍历的数据在HashTable中存在,说明这个数据重复,可以将这个数据删除掉
* @param head
*/
public void deleteDuplecate(Node head){
if (null == head){
System.out.println("链表为空");
return;
}
Hashtable<Integer,Integer> table = new Hashtable<Integer, Integer>();
Node tmp = head;
Node pre = null;
while (tmp != null){
//当前访问的值,在hashtable中存在
if (table.containsKey(tmp.data)){
//若当前遍历的数据在HashTable中存在,说明这个数据重复,可以将这个数据删除掉
pre.next = tmp.next;
size--;
}else{//不包含此值,把这个键值对插入
table.put(tmp.data,1);
pre = tmp;
}
tmp = tmp.next;
}
}
//从链表中删除重复数据
/**
* 对链表进行双重循环遍历,
* 外层循环正常遍历链表,若当前遍历到的结点为curNode
* 内层循环从curNode.next开始遍历,若碰到与curNode结点的值相同的结点,删除掉这个重复的结点
* @param head
*/
public void deleteDuplecate1(Node head){
if (null == head){
System.out.println("链表为空");
return;
}
Node curNode = head;
while (curNode != null){
Node insideNode = curNode;
while (insideNode.next != null){
if (curNode.data == insideNode.next.data){
insideNode.next = insideNode.next.next;
size--;
}else{
insideNode = insideNode.next;
}
}
curNode = curNode.next;
}
}
//如何找出单链表中的倒数第k个元素
public Node findElem(Node head,int k){
if (k < 1){
return null;
}
Node p1 = head;
Node p2 = head;
//开始时候让快慢指针相差k个结点
//前移k-1步
for (int i = 0; i < k - 1 && p1 != null; i++) {
p1 = p1.next;
}
if (p1 == null){
System.out.println("k大于size");
return null;
}
//由于开始时候让快慢指针相差k个结点
//然后同时往前移动到快指针先行指针为null,
//令一个指针所指的位置就是我们想要的
while (p1.next != null){
p1 = p1.next;
p2 = p2.next;
}
return p2;
}
//如何实现链表的反转
public static Node ReverseIteratively(Node head){
Node pre = null;
Node next = null;
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
//递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场
public Node reverse(Node head) {
//递归
if (null == head ||null == head.next)
return head;
Node temp = head.next;
Node newHead = reverse(head.next);
temp.next = head;
head.next = null;
return newHead;
}
//从尾到头输出单链表
public void printListReverse(Node node){
if (node != null){
printListReverse(node.next);
System.out.println(node.data);
}
}
@Test
public void testCase() {
LinkedList list = new LinkedList();
System.out.println("测试头插法");
list.addNodeHead(4);
list.addNodeHead(3);
list.addNodeHead(2);
list.addNodeHead(1);
list.display();
System.out.println("从头到尾输出单链表");
list.printListReverse(list.head);
}
}
