算法
二叉树
二叉树结构
二叉树(binary tree)t是有限个元素的集合(可以为空)。当二叉树非空时,其中有一个称为根的元素,余下的元素(如果有的话)被组成2个二叉树,分别称为t的左子树和右子树
class Node {
private Integer value;
private Node leftNode;
private Node rightNode;
}
前序遍历
中,左,右
/**
* 普通前序遍历
* 不使用栈操作
* @param node
*/
public void commonBefore(Node node){
Node cur = node;
Node pre;
while (cur != null){
if (cur.leftNode == null){
System.out.println(cur.getValue());
cur = cur.rightNode;
}else {
pre = cur.leftNode;
while (pre.rightNode != null && pre.rightNode != cur){
pre = pre.rightNode;
}
if (pre.rightNode == null){
System.out.println(cur.getValue());
pre.rightNode = cur;
cur = cur.leftNode;
}else {
pre.rightNode = null;
cur = cur.rightNode;
}
}
}
}
/**
* 遍历实现 前序
* @param root
*/
public void traverseBefore(Node root){
LinkedList<Node> list = new LinkedList<>();
Node currentNode = root;
//从根开始,一只先访问左子树,再访问右子树,退出某一层级时当前栈顶则为根节点
while (currentNode != null || !list.isEmpty()){
while (currentNode != null){
System.out.println(currentNode.getValue());
list.push(currentNode);
currentNode = currentNode.leftNode;
}
currentNode = list.pop().rightNode;
}
}
/**
* 先序遍历,迭代实现
*/
public void recursiveBefore(Node node){
if (node == null){
return;
}
System.out.println(node.getValue());
recursiveBefore(node.leftNode);
recursiveBefore(node.rightNode);
}
中序遍历
左,中,右
/**
* 普通中序遍历
* 不使用栈操作
* @param node
*/
public void commonMid(Node node){
Node cur = node;
Node pre ;
while (cur != null){
if (cur.leftNode == null){
System.out.println(cur.getValue());
cur = cur.rightNode;
}else {
pre = cur.leftNode;
while (pre.rightNode != null && pre.rightNode != cur){
pre = pre.rightNode;
}
if (pre.rightNode == null){
pre.rightNode = cur;
cur = cur.leftNode;
}else {
System.out.println(cur.getValue());
pre.rightNode = null;
cur = cur.rightNode;
}
}
}
}
/**
* 遍历实现 中序
* @param root
*/
public void traverseMid(Node root){
LinkedList<Node> list = new LinkedList<>();
Node currentNode = root;
//从根开始,一只先访问左子树,再访问父节点,再访问右子树,退出某一层级时当前栈顶则为根节点
while (currentNode != null || !list.isEmpty()){
while (currentNode != null){
list.push(currentNode);
currentNode = currentNode.leftNode;
}
Node self = list.pop();
System.out.println(self.getValue());
currentNode = self.rightNode;
}
}
/**
* 中序遍历,迭代实现
* @param node
*/
public void recursiveMid(Node node){
if (node == null){
return;
}
recursiveMid(node.leftNode);
System.out.println(node.getValue());
recursiveMid(node.rightNode);
}
后序遍历
左,右,中
/**
* 普通后序
* 遍历
* 不使用栈操作
* @param node
*/
public void commonAfter(Node node){
Node virtualNode = new Node(-1);
virtualNode.leftNode = node;
Node cur = virtualNode;
Node pre ;
while (cur != null){
if (cur.leftNode == null){
cur = cur.rightNode;
}else {
pre = cur.leftNode;
while (pre.rightNode != null && pre.rightNode != cur){
pre = pre.rightNode;
}
if (pre.rightNode == null){
pre.rightNode = cur;
cur = cur.leftNode;
}else {
pre.rightNode = null;
reverseVisit(cur.leftNode,pre);
cur = cur.rightNode;
}
}
}
}
private void reverseVisit(Node from, Node to) {
if (from == to){
System.out.println(from.getValue());
return;
}
reverseVisit(from.rightNode,to);
System.out.println(from.getValue());
}
/**
* 遍历实现 后序
* @param root
*/
public void traverseAfter(Node root){
LinkedList<Node> list = new LinkedList<>();
//1 标识仅访问了左节点,2标识左右节点都已经访问了
LinkedList<Integer> visitedState = new LinkedList<>();
Node currentNode = root;
//从根开始,一只先访问左子树,再访问右子树,再访问父节点,退出某一层级时当前栈顶则为根节点
while (currentNode != null || !list.isEmpty()){
while (currentNode != null){
//一层层遍历左节点
list.push(currentNode);
currentNode = currentNode.leftNode;
visitedState.push(1);
}
while (!list.isEmpty() && visitedState.peek() ==2){
visitedState.pop();
System.out.println(list.pop().getValue());
}
if (!list.isEmpty()){
currentNode = list.peek().rightNode;
visitedState.pop();
visitedState.push(2);
}
}
}
/**
* 后序遍历,迭代实现
* @param node
*/
public void recursiveAfter(Node node){
if (node == null){
return;
}
recursiveAfter(node.leftNode);
recursiveAfter(node.rightNode);
System.out.println(node.getValue());
}
二叉树层序遍历
即广度优先
/**
* 层序遍历
* 广度优先遍历
* @param root
*/
public void traverseLevel(Node root){
if (root == null){
return;
}
LinkedList<Node> list = new LinkedList<>();
list.offer(root);
while (!list.isEmpty()){
Node tmp = list.poll();
System.out.println(tmp.getValue());
if (tmp.leftNode != null){
list.offer(tmp.leftNode);
}
if (tmp.rightNode != null){
list.offer(tmp.rightNode);
}
}
}
求树的节点数
/**
* 求树的节点数
* @param root
* @return
*/
public int countNode(Node root){
if (root == null){
return 0;
}
int leftTreeCount = countNode(root.leftNode);
int rightTreeCount = countNode(root.rightNode);
return leftTreeCount + rightTreeCount + 1;
}
求树的叶子节点数
/**
* 求树的叶子节点数
* @param root
* @return
*/
public int countLeafNode(Node root){
if (root == null){
return 0;
}
if (root.leftNode == null && root.rightNode == null){
return 1;
}else {
int leftTreeCount = countLeafNode(root.leftNode);
int rightTreeCount = countLeafNode(root.rightNode);
return leftTreeCount + rightTreeCount;
}
}
求树高度
/**
* 求树高度
* @param root
* @return
*/
public int height(Node root){
if (root == null){
return 0;
}
int leftTreeHeight = height(root.leftNode);
int rightTreeHeight = height(root.rightNode);
return leftTreeHeight > rightTreeHeight?leftTreeHeight+1:rightTreeHeight+1;
}
求第K层的节点数目
/**
* 求第K层的节点数目
* @param root
* @param k
* @return
*/
public int countLevelNode(Node root,int k){
if (root == null || k<1){
return 0;
}
if (k == 1){
return 1;
}
return countLevelNode(root.leftNode,k-1) + countLevelNode(root.rightNode,k-1);
}
比较两个二叉树结构是否相同
/**
* 比较两个二叉树结构是否相同
* @param node1
* @param node2
* @return
*/
public boolean equalStructure(Node node1,Node node2){
if (node1 == null && node2 == null){
return true;
}
if (node1 == null || node2 == null){
return false;
}
boolean left = equalStructure(node1.leftNode,node2.leftNode);
boolean right = equalStructure(node1.rightNode,node2.rightNode);
return left && right;
}
对树进行镜像(翻转)
/**
* 对树进行镜像
* 对于每个节点,交换它的左右孩子
* @param root
*/
public void mirror(Node root){
if (root == null){
return;
}
Node tmp = root.leftNode;
root.leftNode = root.rightNode;
root.rightNode = tmp;
mirror(root.leftNode);
mirror(root.rightNode);
}
求最低公共祖先
/**
* 求最低公共祖先
* @param root
* @param node1
* @param node2
* @return
*/
public Node lowestCommonAncestor(Node root,Node node1,Node node2){
if (root == null){
return null;
}
if (node1 == root || node2 == root){
return root;
}
Node left = lowestCommonAncestor(root.leftNode,node1,node2);
Node right = lowestCommonAncestor(root.rightNode,node1,node2);
if (left != null && right != null){
return root;
}
return left == null ? right:left;
}
求两个节点的最短距离
/**
* 求两个节点的最短距离
* @param node1
* @param node2
* @return
*/
public int distance(Node root,Node node1,Node node2){
Node nodeLCA = lowestCommonAncestor(root,node1,node2);
return nodeLevel(nodeLCA,node1) + nodeLevel(nodeLCA,node2);
}
/**
* 计算子节点所在层级
* @param root
* @param node
* @return
*/
private int nodeLevel(Node root, Node node) {
if (root == null){
return -1;
}
if (root == node){
return 0;
}
int level = nodeLevel(root.leftNode,node);
if (level == -1){
level = nodeLevel(root.rightNode,node);
if (level == -1){
return level;
}
}
return level +1;
}
查找所有祖先
/**
* 查找所有祖先节点
* @param root
* @param node
*/
public boolean allAncestor(Node root,Node node){
if (root == null){
return false;
}
if (root == node){
return true;
}
if (allAncestor(root.leftNode,node) || allAncestor(root.rightNode,node)){
System.out.println(root.getValue());
return true;
}
return false;
}
判断是否为完全二叉树
/**
* 判断是否为完全二叉树
* 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,
* 第 h 层所有的结点都连续集中在最左边,这就是完全二叉树(Complete Binary Tree)
* @param root
* @return
*/
public boolean isPerfect(Node root){
if (root == null){
return true;
}
boolean border = false;
Node tmp;
LinkedList<Node> list = new LinkedList<>();
list.push(root);
while (!list.isEmpty()){
tmp = list.pop();
if (tmp.leftNode == null){
if (tmp.rightNode != null){
return false;
}
}else {
if (border){
return false;
}
list.push(tmp.leftNode);
if (tmp.rightNode == null){
border = true;
}else {
list.push(tmp.rightNode);
}
}
}
return true;
}
什么是平衡二叉树,什么是红黑二叉树
平衡二叉树定义(AVL):它或者是一颗空树,或者具有以下性质的二叉树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树
红黑树,本质上来说就是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。红黑树有如下5条性质:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点
链表
单向链表结构
只可向一端遍历
public class SingleLinkDemo {
private int size;
private Node first;
private Node last;
//省略getter 和 setter
public void addTail(Integer value){
Node node = new Node(value);
if(size==0){
first = node;
last = node;
}else{
last.setNext(node);
last = node;
}
size++;
}
public void addHead(Integer value) {
Node node = new Node(value);
if(size==0){
first = node;
last = node;
}else{
node.setNext(first);
first = node;
}
size++;
}
public void add(int index,Integer value){
if (index <size){
if (size == 0){
Node node = new Node(value);
first = node;
last = node;
size++;
}else if (index == 0){
addHead(value);
}else if (index ==size -1){
addTail(value);
}else{
Node node = new Node(value);
Node tmp = getNode(index);
node.setNext(tmp.getNext());
tmp.setNext(node);
size++;
}
}else {
throw new IndexOutOfBoundsException("插入位置无效或超出链表长度");
}
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
Node node = first;
while (node != null){
System.out.println(node.getValue());
node = node.getNext();
}
return stringBuilder.toString();
}
public Node getNode(int index) {
if (index > size -1){
throw new IndexOutOfBoundsException("超出链表长度");
}
Node node = first;
for (int i=0;i<index;i++){
node = node.getNext();
}
return node;
}
static class Node {
private Integer value;
private Node next;
//省略getter 和setter
}
}
单向链表的逆序排列
/**
* 反转链表
*/
public void reverseLink(){
Node tmp = first;
last = tmp;
Node next = first.getNext();
for (int i = 0;i<size-1;i++){
Node nextNode = next.getNext();
next.setNext(tmp);
tmp = next;
next = nextNode;
}
last.setNext(null);
first = tmp;
}
双向链表的结构
可以向两端遍历
public class DoubleLinkDemo {
private int size = 0;
private Node first;
private Node last;
//省略getter 和 setter
class Node{
private int value;
private Node pre;
private Node next;
//省略getter 和 setter
}
}
双向链表的操作
public void addLast(Integer value){
Node node = new Node(value);
if(size==0){
first = node;
last = node;
}else{
node.setPre(last);
last.setNext(node);
last = node;
}
size++;
}
public void addFirst(Integer value) {
Node node = new Node(value);
if(size==0){
first = node;
last = node;
}else{
first.setPre(node);
node.setNext(first);
first = node;
}
size++;
}
public void add(int index,Integer value){
if (index <size){
if (size == 0){
Node node = new Node(value);
first = node;
last = node;
size++;
}else if (index == 0){
addFirst(value);
}else if (index ==size -1){
addLast(value);
}else{
Node node = new Node(value);
Node tmp = getNode(index);
node.setPre(tmp);
node.setNext(tmp.getNext());
tmp.setNext(node);
size++;
}
}else {
throw new IndexOutOfBoundsException("插入位置无效或超出链表长度");
}
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
Node node = first;
while (node != null){
System.out.println(node.getValue());
node = node.getNext();
}
return stringBuilder.toString();
}
public Node getNode(int index) {
if (index > size -1){
throw new IndexOutOfBoundsException("超出链表长度");
}
Node node = first;
for (int i=0;i<index;i++){
node = node.getNext();
}
return node;
}
数组
冒泡排序
一直比较取最大的放在最后
/**
* 冒泡排序
* 一直比较取最大的放在最后
* @param ts
*/
public static void bubbingSort(Integer[] ts){
if (ts.length == 1){
return;
}
int tmp = 0;
for (int i = 0;i<ts.length;i++){
for (int j = 0;j<ts.length-1-i;j++){
if (ts[j]>ts[j+1]){
tmp = ts[j];
ts[j] = ts[j+1];
ts[j+1] = tmp;
}
}
}
}
快速排序
在数据集之中,选择一个元素作为”基准”(pivot)。所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。这个操作称为分区 (partition) 操作,分区操作结束后,基准元素所处的位置就是最终排序后它的位置。对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止
/**
* 快速排序
* 在数据集之中,选择一个元素作为”基准”(pivot)。
* 所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。这个操作称为分区 (partition) 操作,
* 分区操作结束后,基准元素所处的位置就是最终排序后它的位置。
* 对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
* @param ts
*/
public static void fastSort(Integer[] ts){
if (ts.length>0){
fastStart(ts,0,ts.length -1);
}
}
/**
* 内部递归
* @param ts
* @param beginIndex
* @param endIndex
*/
private static void fastStart(Integer[] ts, int beginIndex, int endIndex) {
if (beginIndex < endIndex){
int midIndex = getMidIndex(ts,beginIndex,endIndex);
fastStart(ts,beginIndex,midIndex-1);
fastStart(ts,midIndex +1,endIndex);
}
}
/**
* 左右移动
* @param ts
* @param beginIndex
* @param endIndex
* @return
*/
private static int getMidIndex(Integer[] ts, int beginIndex, int endIndex) {
int tmp = ts[beginIndex];
while (beginIndex < endIndex){
//从后开始找,直到找到比基准数值小的
while (ts[endIndex] >= tmp && beginIndex < endIndex){
endIndex--;
}
//小的往前移动,这时候后边出现了一个重复了,需要有一个大的去填补
ts[beginIndex] = ts[endIndex];
//从前边开始找,直到找到比基准数值大的
while (ts[beginIndex] <tmp && beginIndex < endIndex){
beginIndex++ ;
}
//大的往后移动,填补之前的空缺
ts[endIndex] = ts[beginIndex];
}
//最终,把基准值放在中间
ts[beginIndex] = tmp;
return beginIndex;
}
选择排序
/**
* 选择排序
* 在未排序序列中找到最小元素,存放到排序序列的起始位置
* @param ts
*/
public static void selectSort(Integer[] ts){
if (ts.length <=1){
return;
}
int tmp = 0;
for (int i =0;i<ts.length;i++){
//当前最小的值所在索引
int k = i;
//找到剩余列表中,最小的
for (int j = ts.length -1 ;j>i;j--){
if (ts[j]<ts[k]){
k = j;
}
}
//把最小的放前面
tmp = ts[i];
ts[i] = ts[k];
ts[k] = tmp;
}
}
插入排序
/**
* 插入排序
* 从第一个元素开始,该元素可以认为已经被排序
* 取出下一个元素,在已经排序的元素序列中从后向前扫描
* 如果前一个比当前值大,则把前一个往后移一位,直到不大的时候,此时,当前值应该放在此位置
* @param ts
*/
public static void insertionSort(Integer[] ts){
if (ts.length <=1){
return;
}
int tmp = 0;
int j =0;
for (int i = 0;i<ts.length;i++){
tmp = ts[i];
for (j = i;j>0 && tmp <ts[j-1];j--){
ts[j] = ts[j-1];
}
ts[j] = tmp;
}
}
希尔排序
/**
* 希尔排序
* 先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,
* 然后依次缩减增量再进行排序,
* 待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序
* @param ts
*/
public static void hillSort(Integer[] ts){
int j = 0;
int tmp = 0;
for (int increment = ts.length/2;increment>0;increment/=2){
for (int i = increment;i<ts.length;i++){
tmp = ts[i];
for (j = i;j>=increment;j-=increment){
if (tmp<ts[j-increment]){
ts[j] = ts[j-increment];
}else {
break;
}
}
ts[j] = tmp;
}
}
}
归并排序
/**
* 归并排序
* 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,
* 即把待排序序列分为若干个子序列,每个子序列是有序的。
* 然后再把有序子序列合并为整体有序序列
* @param ts
*/
public static void mergeSort(Integer[] ts,int beginIndex,int endIndex){
int midIndex = (beginIndex + endIndex)/2;
if (beginIndex < endIndex){
mergeSort(ts,0,midIndex);
mergeSort(ts,midIndex +1,endIndex);
merge(ts,beginIndex,midIndex,endIndex);
}
}
private static void merge(Integer[] ts, int beginIndex, int midIndex, int endIndex) {
int[] tmp = new int[endIndex-beginIndex +1];
int i = beginIndex;
int j = midIndex +1;
int k =0;
//把左右中最小的依次放在tmp 中,直到某一边被移动完
while (i<=midIndex && j<=endIndex){
if (ts[i] < ts[j]){
tmp[k++] = ts[i++];
}else {
tmp[k++] = ts[j++];
}
}
//把左边剩余的依次放到tmp
while (i<=midIndex){
tmp[k++] = ts[i++];
}
//把右边剩余的依次放到tmp
while (j<=endIndex){
tmp[k++] = ts[j++];
}
//替换到原数组中
for (int m=0;m<tmp.length;m++){
ts[m + beginIndex] = tmp[m];
}
}
堆排序
/**
* 堆排序
* 将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。
* 将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),
* 然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次最大值。
* 如此反复执行,就能得到一个有序序列了
* @param ts
*/
public static void heapSort(Integer[] ts){
for (int i=ts.length/2;i>=0;i--){
heapAdjust(ts,i,ts.length);
}
for (int j = ts.length -1 ;j>0;j--){
int tmp = ts[0];
ts[0] = ts[j];
ts[j] = tmp;
heapAdjust(ts,0,j);
}
}
private static void heapAdjust(Integer[] ts, int index, int length) {
int child = 0;
int father = 0;
for (father = ts[index];leftChild(index)<length;index=child){
child = leftChild(index);
if (child != length -1 && ts[child] < ts[child +1]){
child++;
}
if (father <ts[child]){
ts[index] = ts[child];
}else {
break;
}
}
ts[index] = father;
}
private static int leftChild(int index) {
return 2 * index + 1;
}
数组求逆序对
在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数
/**
* 逆序
* @param ts
*/
public static void reverseSelf(Integer[] ts){
for (int i = 0,j=ts.length-1;i<j;i++,j--){
int tmp = ts[i];
ts[i] = ts[j];
ts[j] = tmp;
}
}
/**
* 使用JDK工具类
* @param ts
*/
public static void reverseTools(Integer[] ts){
List list = Arrays.asList(ts);
Collections.reverse(list);
ts = (Integer[]) list.toArray();
}
整形数组的逆序数
/**
* 求int数组逆序数
* 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。
* 逆序数可如下计算:标出每个数右面比它小的数的个数,它们的和就是逆序数.
* 例如求436512的逆序数:t(436512)=3+2+3+2+0+0=10.一个排列中逆序的总数就称为这个排列的逆序数。
* 逆序数为偶数的排列称为偶排列;
* 逆序数为奇数的排列称为奇排列。如2431中,21,43,41,31是逆序,逆序数是4,为偶排列
* 使用归并排序
*/
public static int inverseCount(Integer[] ts,int beginIndex,int endIndex){
int midIndex = (beginIndex + endIndex)/2;
if (beginIndex < endIndex){
int left = inverseCount(ts,0,midIndex);
int right = inverseCount(ts,midIndex +1,endIndex);
int count = mergeCount(ts,beginIndex,midIndex,endIndex);
return left + right + count;
}
return 0;
}
private static int mergeCount(Integer[] ts, int beginIndex, int midIndex, int endIndex) {
int[] tmp = new int[endIndex-beginIndex +1];
int i = beginIndex;
int j = midIndex +1;
int k =0;
int count = 0;
//把左右中最小的依次放在tmp 中,直到某一边被移动完
while (i<=midIndex && j<=endIndex){
if (ts[i] <= ts[j]){
tmp[k++] = ts[i++];
}else {
//如果前面的大于后面的,则前面剩余的值都大于后边当前值,则逆序数需要加上之前的全部数量
count += j-i;
for (int m = i;m<=midIndex;m++){
System.out.println(ts[m] + "-"+ts[j]);
}
tmp[k++] = ts[j++];
}
}
//把左边剩余的依次放到tmp
while (i<=midIndex){
tmp[k++] = ts[i++];
}
//把右边剩余的依次放到tmp
while (j<=endIndex){
tmp[k++] = ts[j++];
}
//替换到原数组中
for (int m=0;m<tmp.length;m++){
ts[m + beginIndex] = tmp[m];
}
return count;
}
二分查找的算法
/**
* 递归二分查找
* @param ts
* @param key
* @param beginIndex
* @param endIndex
* @return
*/
public static Integer binarySearch(Integer[] ts,int key,int beginIndex,int endIndex){
if (beginIndex == endIndex){
if (ts[beginIndex] == key){
return beginIndex;
}else {
return null;
}
}else{
int midIndex = (beginIndex + endIndex) /2;
if (ts[midIndex] == key){
return midIndex;
}
if (ts[midIndex] < key){
return binarySearch(ts,key,midIndex +1,endIndex);
}else {
return binarySearch(ts,key,beginIndex,midIndex);
}
}
}
/**
* 非递归实现二分查找
* @param ts
* @param key
* @return
*/
public static Integer commonBinarySearch(Integer[] ts,int key){
int beginIndex = 0;
int endIndex = ts.length -1;
int midIndex ;
while (beginIndex <= endIndex){
midIndex = (endIndex + beginIndex) /2;
if (midIndex == beginIndex){
if (ts[midIndex] == key){
return midIndex;
}else {
break;
}
}else {
if (ts[midIndex] < key){
beginIndex = midIndex + 1;
}else {
endIndex = midIndex;
}
}
}
return null;
}
其他
1个整数的倒序输出
循环求余
2.5亿个整数的倒序输出(不重复)
使用bitmap,bitmap 是使用0,1 来标识对应值是否存在以及存在次数,例如 ,0 标识不存在,1标识存在,或者00标识不存在,01 标识出现一次,10标识出现多次。
给定2.5亿个整数(无符号),如果直接加载这2.5亿个整数,则需要2.5亿 * 4 * 8 bit ,而我们使用bitmap 则仅仅需要2.5亿个bit 即可大概是512M,我们分段遍历这2.5亿个整数,如果存在则在对应的bi位上设为1,默认为0,然后逆序遍历整个bit数组,然后输出对应位上的值。
一个文件,有45亿个阿拉伯数字,如何进行去重啊如何找出最大的那个数
分治 + bitmap ,分治 + 最大/最小堆
给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中
可以用位图/Bitmap的方法,申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在
2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5 亿个整数
将bit-map 扩展一下,用2bit 表示一个数即可,00 表示未出现,01 表示出现一次,10 表示出现2 次及以上,在遍历这些数的时候,如果对应位置的值是0 ,则将其置为01 ;如果是01 ,将其置为10 ;如果是10 ,则保持不变。或者我们不用2bit 来进行表示,我们用两个bit-map 即可模拟实现这个2bit-map ,都是一样的道理
10亿个正整数找出重复次数最多的100个整数
分治 + hashMap/分治 + 最大/最小堆
找出给定字符串中最长回文
如果一个字符串正着读和反着读是一样的,那它就是回文串
方法一,遍历所有子串
方法二,中轴查找,我们知道整个字符串中的所有字符,以及字符间的空隙,都可能是某个回文子串的对称轴位置。可以遍历这些位置,在每个位置上同时向左和向右扩展,直到左右两边的字符不同,或者达到边界。
方法三,Manacher 算法
/**
* manacher算法求取最大回文
* 算法思想
* 1 把偶数、奇数长的字符序列变成奇数长度 (所有的空隙位置(包括首尾)插入同样的符号)
* 2 创建一个与字符串等长的数组,用来记录字符序列相应位置上字符的最长回文半径,半径为1时默认为字符本身
* 3 然后以每个字符为中轴遍历字符序列,之后求数组的最大值即为最大的半径,即为最长的回文半径
* @param s
*/
public static void doManacher(String s) {
//在字符串两头和质检添加特殊字符转成奇数长度,原理:奇数+奇数+1=奇数,偶数+偶数+1=奇数。
StringBuffer sb = new StringBuffer();
sb.append("#");
for (int i = 0; i < s.length(); i++) {
sb = sb.append(s.substring(i, i + 1)).append("#");
}
s = sb.toString();
// 以每个字符为轴求最长回文串半径,其中半径=1表示字符本身。
int[] p = new int[s.length()];
int left, right = 0;
for (int i = 0; i < s.length(); i++) {
int len = 1;
for (left = i - 1, right = i + 1; left >= 0 && right <= (2 * i) && right < s.length(); left--, right++) {
if (s.charAt(left) == s.charAt(right)) {
len = len + 1;
continue;//如果匹配成功就继续
} else {
break;//不成功就跳出循环
}
}
p[i] = len;
}//end wai for
//求最大的p[i]值
int pos, maxValuePos = 0;
for (int i = 0; i < p.length - 1; i++) {
pos = i;
for (int j = i + 1; j < p.length; j++) {
if (p[i] < p[j]) {
pos = j;
int tep = p[i];
p[i] = p[pos];
p[pos] = tep;
}
if (i == 0){
maxValuePos = pos;
}
}
}
//求得的回文串一定是奇数长度
int realLen = ((p[0] * 2 - 1) - 1) / 2;//最长回文串的长度,去掉其他字符
System.out.println("最长的回文串长度为:" + realLen);
//求最长回文串内容
String huiwen;
StringBuffer realHuiwen = new StringBuffer();
if (realLen == 1) {
System.out.println("最长回文串为:" + s.charAt(maxValuePos));
} else {
// 截出来
huiwen = s.substring((maxValuePos + 1 - p[0]), maxValuePos + p[0]);
// 去掉辅助字符
for (int j = 0; j < huiwen.length(); j++) {
if (j % 2 != 0){
realHuiwen = realHuiwen.append(huiwen.charAt(j));
}
}
System.out.println("最长回文串为:" + realHuiwen.toString());
}
}
/**
* 最长回文
* 中心扩展
* 因为回文字符串是以中心轴对称的,所以如果我们从下标 i 出发,用2个指针向 i 的两边扩展判断是否相等,
* 那么只需要对0到n-1的下标都做此操作,就可以求出最长的回文子串。
* 但需要注意的是,回文字符串有奇偶对称之分,即”abcba”与”abba”2种类型,因此需要在代码编写时都做判断
* @param s
* @return
*/
public static String longPalindrome2(String s) {
if (s.isEmpty()) {
return null;
}
if (s.length() == 1) {
return s;
}
String longest = s.substring(0, 1);
for (int i = 0; i < s.length(); i++) {
// get longest palindrome with center of i
String tmp = helper(s, i, i);
if (tmp.length() > longest.length()) {
longest = tmp;
}
// get longest palindrome with center of i, i+1
tmp = helper(s, i, i + 1);
if (tmp.length() > longest.length()) {
longest = tmp;
}
}
return longest;
}
public static String helper(String s, int begin, int end) {
while (begin >= 0 && end <= s.length() - 1
&& s.charAt(begin) == s.charAt(end)) {
begin--;
end++;
}
String subS = s.substring(begin + 1, end);
return subS;
}
/**
* 最长回文
* 动态规划
* 从左开始遍历,然后不断的从原字符串中拿出1到length-1长度的字串,进行判断
* 这里用一个二维数组来表示回文字符串的起始位置和结束位置,值1代表的是回文字符串,0代表不是
* @param s
* @return
*/
public static String longPalindrome(String s) {
if (s == null){
return null;
}
if(s.length() <=1){
return s;
}
int maxLen = 0;
String longestStr = null;
int length = s.length();
int[][] table = new int[length][length];
//every single letter is palindrome
for (int i = 0; i < length; i++) {
table[i][i] = 1;
}
printTable(table);
//two consecutive same letters are palindrome
for (int i = 0; i <= length - 2; i++) {
if (s.charAt(i) == s.charAt(i + 1)){
table[i][i + 1] = 1;
longestStr = s.substring(i, i + 2);
}
}
System.out.println(longestStr);
printTable(table);
//condition for calculate whole table
for (int l = 3; l <= length; l++) {
for (int i = 0; i <= length-l; i++) {
int j = i + l - 1;
if (s.charAt(i) == s.charAt(j)) {
table[i][j] = table[i + 1][j - 1];
if (table[i][j] == 1 && l > maxLen) {
longestStr = s.substring(i, j + 1);
}
} else {
table[i][j] = 0;
}
printTable(table);
}
}
return longestStr;
}
public static void printTable(int[][] x){
for(int [] y : x){
for(int z: y){
System.out.print(z + " ");
}
System.out.println();
}
System.out.println("------");
}
求字符串中指定字符出现的次数
public int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索,每搜索一次,下次搜索就从这个索引开始往后搜索