线性表
顺序存储结构
顺序表:内存地址是连续的
a1,a2,a3,a4.... ai,ai+1,an
a1是a2的前驱,ai+1 是ai的后继,a1没有前驱,an没有后继
n为线性表的长度 ,若n==0时,线性表为空表优点:
尾插效率高,支持随机访问。
缺点:
中间插入或者删除效率低。
应用:
数组
ArrayList
蛮力法排序:
蛮力法(brute force method,也称为穷举法或枚举法)
是一种简单直接地解决问题的方法,
常常直接基于问题的描述,
所以,蛮力法也是最容易应用的方法。(数据比较少的时候)
但是,用蛮力法设计的算法时间特性往往也是最低的,(数据过多的时候)
典型的指数时间算法一般都是通过蛮力搜索而得到的 。(即输入资料的数量依线性成长,所花的时间将会以指数成长)
冒泡排序 适用于3-5个数据
/**
* 冒泡排序
* 相邻的左边比右边大的就互相交换位置,一轮过后最大的数就到了顺序表的末尾
* @param array
*/
public static void sort_bubble(int[] array){
if(array == null || array.length == 0){
return;
}
for(int i = array.length -1 ; i > 0 ; i--){
boolean flag = true;
for(int j = 0; j < i ; j++){
if(array[j] > array[j+1]){
array[j] = array[j]^array[j+1];
array[j+1] = array[j]^array[j+1];
array[j] = array[j]^array[j+1];
flag = false;
}
}
if(flag){
break;
}
}
}
选择排序 适用于3-5个数据
/**
* 选择排序 快速排序的基础
*
* @param array
*/
public static void sort_select(int[] array){
if(array == null || array.length == 0){
return;
}
for(int i = 0 ; i < array.length - 1; i++){
int index = i;
for(int j = i + 1; j < array.length;j++){
if(array[j] < array[index]){
index = j;
}
}
if(index != i){
array[index] = array[i]^array[index];
array[i] = array[i]^array[index];
array[index] = array[i]^array[index];
}
}
}
链式存储结构
线性表的链式存储结构:特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的
优点:
插入或者删除效率高。
缺点:
不支持随机访问。分为:
1.单链表
2.单循环链表
3.双链表
4.双向循环链表
单链表
单链表的每一个数据可以看做是一个节点。
节点由 数据域
+指针域
组成
单循环链表
单链表的最后一个节点的指针域指向第一个数据
双链表
前指针域
+数据域
+后 指针域
组成。单链表查询只能从前往后进行查找,双链表可以支持从后往前查找,所以优化了查询效率。
双向循环链表
参照 单循环链表
下面附上我自己写的自定义的双向链表代码,仅供大家参考:
package com.xx.lists;
/**
* 练习
* 自定义的双向链表
* @author Lixin
*
* @param <E>
*/
public class MyLinkedList<E> {
private Node<E> first;
private Node<E> last;
public int size;
// 添加
public void add(E item){
addLast(item);
}
// 在最后添加一个数据
public void addLast(E item){
Node<E> node = new Node(last,item,null);
Node<E> l = last;
last=node;
if(l == null){
// 此前链表为空
first = node;
}else{
l.next = node;
}
size++;
}
// 在具体位置添加
public void add(int index, E item){
if(index < 0 || index > size){
return;
}
if(index == size){
addLast(item);
}else{
Node<E> oldNode = node(index);
if(oldNode == null){
throw new NullPointerException();
}
Node<E> preNode = oldNode.pre;
Node<E> newNode = new Node<E>(preNode,item,oldNode);
// old
oldNode.pre = newNode;
// pre
if(preNode != null){
preNode.next = newNode;
}else{
first = newNode;
}
size++;
}
}
// 根据下标位置找到节点数据 可以看到链表的查找是比较麻烦的
public E get(int index){
Node<E> node = node(index);
if(node!=null){
return node.item;
}
return null;
}
// 删除
public void remove(int index){
Node<E> node = node(index);
if(node == null){
return;
}
Node<E> nodePre = node.pre;
Node<E> nodeNext = node.next;
if(nodePre == null){
first = nodeNext;
}else{
nodePre.next = nodeNext;
}
node.pre = null;
if(nodeNext != null){
nodeNext.pre = nodePre;
}else{
last = nodePre;
}
node.next = null;
size--;
}
private Node<E> node(int index){
if(index<0 || index>(size - 1)){
throw new IndexOutOfBoundsException();
}
Node<E> node ;
// size >> 1 == size/2
if(index < (size>>1)){
node = first;
for(int i = 0;i<index;i++){
node = node.next;
}
}else{
node = last;
for(int i = size - 1;i > index;i--){
node = node.pre;
}
}
return node;
//如果index在整个链表的前半部分
// if(index<(size>>1)){ //1000 100 10
// Node<E> node=first;
// for (int i = 0; i < index; i++) {
// node=node.next;
// }
// return node;
// }else{
// Node<E> node=last;
// for (int i = size-1; i > index; i--) {
// node=node.pre;
// }
// return node;
// }
}
// 节点
private class Node<E>{
public E item;
// 前一个节点
public Node<E> pre;
// 下一个节点
public Node<E> next;
public Node(Node<E> pre,E item,Node<E> next){
this.item = item;
this.pre = pre;
this.next = next;
}
}
}
栈和递归
栈
栈是限定仅在表尾进行插入和删除操作的线性表。
允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。
栈又称为后进先出
的线性表
栈的实现方式:
顺序方式:Stack.java类
链式方式:last = 栈顶
入栈方式
出栈方式
逆波兰表达式: 高级语言中的运算方法原理,比较麻烦,有时间再开个专题讲解吧,有兴趣的同学也可以百度去查一下相关资料。
大概逻辑是:中缀表达式(如 1+1) (依照数字输出,运算符优先级低出栈,运算符高入栈的原则)转成后缀表达式(1 1 + )
计算时,数字入栈,遇到运算符号就取栈内的两个数字进行计算。
递归
程序调用自身的编程技巧称为递归(recursion)。
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,
它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,
递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的能力在于用有限的语句来定义对象的无限集合。
一般来说,递归需要有边界条件
、递归前进段
和递归返回段
。
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
执行特点:每次递归调用的方法会放入方法栈中,然后从栈顶依次调用。
调用自己一次的情况:调用位置前面的代码是正循环,调用位置后面的代码是反循环
调用自己两次的情况:一个二叉树的中序遍历过程
下面为大家介绍一个用递归算法实现的一个程序:
汉诺塔算法:有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问至少需要多少次移动
public class RecursionTest {
public static void main(String[] arg0){
hanoi(3,1,2,3);
}
/**
* @param n 盘子的个数
* @param start 开始的柱子
* @param middle 中介柱子
* @param end 结果柱子
*/
public static void hanoi(int n,int start,int middle,int end){
if(n<=1){
System.out.println(start+"----->"+end);
}else{
hanoi(n-1,start,end,middle);
System.out.println(start+"----->"+end);
hanoi(n-1,middle,start,end);
}
}
}