一. 数据结构-链表
题目1:删除链表中重复的节点
代码实现:
public class DeleteDuplication {
//原链表:0->1->1->2->3
//处理后的链表:0->2->3
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null)
return null;
//方法1:使用HashMap实现---》写不下去了 // int count = 0; // Map<Object,Integer> map = new HashMap<>(); // //将链表中节点的值塞进集合里面 // ArrayList arrayList = new ArrayList(); // while (pHead != null){ // arrayList.add(pHead.val); // pHead = pHead.next; // } // //判断重复的节点 // for (Object i : arrayList){ // if (!map.containsKey(i)){ // map.put(i,1); // } // else { // map.put(i,map.get(i)+1); // if () // } // }
//保留pHead和first的初始值
ListNode first = new ListNode(-1);
first.next = pHead;
ListNode p = pHead;
//指向前一个节点
ListNode preNode = first;
//preNode->p->p.next
while (p != null && p.next != null){
//若当前节点数值与下一个节点数值相等
if (p.val == p.next.val){
int val = p.val;
//继续向后查找
while (p != null && p.val == val){
p = p.next;
}
//删除重复的节点
preNode.next = p;
}
//如果当前节点数值与下一个节点数值不相等
else {
preNode = p;
p = p.next;
}
}
return first.next;
}
public static void main(String[] args) {
//创建一个有重复节点的有序链表
ListNode p1 = new ListNode(0);
ListNode p2 = new ListNode(1);
ListNode p3 = new ListNode(1);
ListNode p4 = new ListNode(2);
ListNode p5 = new ListNode(3);
p1.next = p2;
p2.next = p3;
p3.next = p4;
p4.next = p5;
p5.next = null;
DeleteDuplication d = new DeleteDuplication();
ListNode result = d.deleteDuplication(p2);
System.out.println("链表的第一个节点数值是:" + result.val);
System.out.println("链表的第二个节点数值是:" + result.next.val); } }
//算法复杂度:O(n2)
//算法思路:定义一个节点指向头节点,从第一个节点开始遍历,若是重复节点,则删除重复节点,将头节点指向不重复节点
题目2. 找出链表中环的入口节点
代码实现:
public class EntryNodeOfLoop {
// public ListNode entryNodeOfLoop(ListNode pHead) {
// //方法1:使用HashSet实现,无序不能重复
// if (pHead == null)
// return null;
//
// HashSet<ListNode> hashSet = new HashSet<>();
// while (pHead != null){
// //算法思路:从链表的第一个节点开始遍历,若某个元素出现次数超过一次,则输出
// if (!hashSet.add(pHead)){
// return pHead;
// }
// pHead = pHead.next;
// }
// return null;
// }
public ListNode entryNodeOfLoop(ListNode pHead) {
if (pHead == null)
return null;
//方法2:计算循环
ListNode fast = pHead;
ListNode slow = pHead;
while (fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
//当快指针和慢指针相遇时
if (fast == slow){
fast = pHead;
//再次相遇
while (fast != slow){
fast = fast.next;
slow = slow.next;
}
if (fast == slow){
return slow;
}
}
}
return null;
}
public static void main(String[] args){
//创建一个链表
ListNode p1 = new ListNode(0);
ListNode p2 = new ListNode(1);
ListNode p3 = new ListNode(2);
ListNode p4 = new ListNode(3);
ListNode p5 = new ListNode(4);
ListNode p6 = new ListNode(5);
p1.next = p2;
p2.next = p3;
p3.next = p4;
p4.next = p5;
p5.next = p6;
p6.next = p4;
EntryNodeOfLoop en = new EntryNodeOfLoop();
ListNode result = en.entryNodeOfLoop(p1);
System.out.println(result.val);
}
}
//方法1:空间复杂度:占用内存:9460k;时间复杂度:O(n)
//方法2:怎么实现的?https://www.cnblogs.com/fankongkong/p/7007869.html 空间复杂度:9408k,时间复杂度:?
二. 线程的创建和启动
第一种方法:
/**
* @Author: qw
* @Date: 2019/3/31
* @Description:**继承Thread类创建线程类**
*/
public class FirstThread extends Thread{
private int i;
//第一步:重写run方法,run方法的方法体就是线程执行体
public void run(){
for ( ; i < 100; i++){
//获取当前线程的名字
System.out.println(this.getName() + "" + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + "" + i);
if (i == 20){
//第二、三步:创建并启动第一个线程
new FirstThread().start();
//创建并启动第二个线程
new FirstThread().start();
}
}
}
}
第二种方法:
/**
* @Author: qw
* @Date: 2019/3/31
* @Description:**实现Runnable接口创建线程类**
*/
public class SecondThread implements Runnable{
private int i;
//第一步:重写Runnable接口的run()方法
public void run(){
for ( ; i < 100; i++){
System.out.println(Thread.currentThread().getName() + "" + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + "" + i);
if (i == 20){
//第二步:创建Runnable对象
SecondThread st = new SecondThread();
//第三步:将Runnable对象作为实参target创建线程对象
new Thread(st,"新线程1").start();
new Thread(st,"新线程2").start();
}
}
}
}
第三种方法:
/**
-
@Author: qw
-
@Date: 2019/3/31
-
@Description:使用Callable和Future创建线程
*/public class ThirdThread {
public static void main(String[] args) {
ThirdThread rt = new ThirdThread();
//第一步:使用Lambda表达式创建一个Callable对象,然后将该实例包装成一个FutureTask对象,作为创建线程对象的实参传入
FutureTask task = new FutureTask((Callable)()->{
int i = 0;
for ( ; i < 100; i++){
System.out.println(Thread.currentThread().getName() + “的循环变量i的值:” + i);
}
return i;
});for (int i = 0; i < 100; i++){ System.out.println(Thread.currentThread().getName() + "循环变量i的值:" + i); if (i == 20){ //第二步:创建线程的对象并启动 new Thread(task,"有返回的线程").start(); } } try { //获取线程返回值 System.out.println("子线程的返回值:" + task.get()); } catch (Exception ex){ ex.printStackTrace(); } } }
小结:三种方法的优缺点:
第一种方法:
优点:编程稍微简洁
缺点:1. 线程类不能继承其他类;2. 多个线程间不能共享实现类FirstThread的实例变量
第二、三种方法:
优点:1. 线程类可继承其他类;2. 多个线程间共享target对象,将CPU、代码和数据分开,体现了面向对象的思想。
缺点:编程稍微复杂