算法初学(第二天)

1. 给定一个数组和一个数num,将比num小的数放在数组的左边,大的数放右边。

   要求额外空间复杂度为O(1),时间复杂符为O(N)  即只借助有限个变量,只进行一次常数次操作

   1.1 借助一个变量x,此变量指向数组的最前面-1,0~x表示小于等于num的范围

   1.2 判断这个数比num大还是小,如果大的话保持不变,如果小的话,与索引为x+1的数进行交换

   1.3 并且x+1扩大范围

   1.4 代码:

int[] arr = {1,2,6,7,2,4};
int num = 3;
int x = -1;
for(int i=0;i<arr.length;i++){
    if(arr[i]<num){
        int temp = arr[x+1];
        arr[++x] = arr[i];
        arr[i] = temp;

    }
}

2. 荷兰国旗问题  

给定一个数组arr, 和一个数num, 请把小于num的数放在数组的左边, 等于num的数放在数组的中间, 大于num的数放在数组的右边。 要求额外空间复杂度O(1), 时间复杂度O(N)

  2.1 与之前那道题类似,不过本题需要多写一个变量y=N,y~N-1表示大于num的数的范围

  2.2 从左边开始,如果小,则当前数与x+1位置的数进行交换;x再加1,当前数+1

                              如果等于则保持不变;当前数+1

                              如果大,则当前数与y-1位置的数进行交换;y再加1,当前数保持不变

  2.3 代码:

       int[] arr = {1,2,6,3,7,2,4};
        int num = 3;
        int x = -1;
        int y = arr.length;
        for(int i=0;i<y;i++){
            if(arr[i]<num){
                int temp = arr[x+1];
                arr[++x] = arr[i];
                arr[i] = temp;
            }
            else if(arr[i]>num){
                int temp = arr[y-1];
                arr[--y] = arr[i];
                arr[i] = temp;
                i--;
            }
        }


3. 随机快速排序  O(NlogN)  额外空间复杂度O(logN)

     3.1 以第一个数为基准,将数组分为小于第一个数的数组,大于第一个的数组,中间的为等于第一个数的数组

     3.2 再将分别以数第一个为基准,循环

     3.3 代码:

	public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}

	public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
			int[] p = partition(arr, l, r);
			quickSort(arr, l, p[0] - 1);
			quickSort(arr, p[1] + 1, r);
		}
	}

	public static int[] partition(int[] arr, int l, int r) {
		int less = l - 1;
		int more = r;
		while (l < more) {
			if (arr[l] < arr[r]) {
				swap(arr, ++less, l++);
			} else if (arr[l] > arr[r]) {
				swap(arr, --more, l);
			} else {
				l++;
			}
		}
		swap(arr, more, r);
		return new int[] { less + 1, more };
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

3.  堆是一颗完全二叉树。包括满二叉树(任何一个非叶节点(叶节点表示二叉树的最终节点,即最后一排的节点)都是有左边和右边的)和完全二叉树(前面的是满二叉树,最后一排从左往右依次补齐)   如下图,左边是完全二叉树,右边是满二叉树。

   

2.1数组可以变为一颗完全二叉树

   任意一个下标i,该节点的左边是2*i+1,右边是2*i+2,父节点是(i-1)/2  除法为地板除,只保留整数位

2.2 大根堆

  任意一个树的根都是最大的。如整个树的根是最大的,其中的子树中,根也是最大的。

2.3 小根堆

  任意一个树的根都是最小的。如整个树的根是最小的,其中的子树中,根也是最小的。

2.4 堆排序 (将数组从小到大排序)

   2.4.1 先把数组变成大根堆,然后将数组的最后一个元素与第一个元素交换,此时最后一个元素即为最大元素

   2.4.2 再将除了最后一个元素的数组进入大根堆进行下沉,循环

   2.4.3 代码:

    public static void heapSort(int[] arr){
        if(arr==null|arr.length<2){
            return;
        }
       for(int i=0;i<arr.length;i++){    //先进行大根堆排序
           heapInsert(arr,i);
       }
       int size = arr.length;
       swap(arr,0,--size);
       while(size>0){
           
           heapInfer(arr,0,size);        //调整第一个元素在大根堆的位置
           swap(arr,0,--size);           //数组中最后一个元素与第一个元素交换
       }
    }
    public static void heapInfer(int[] arr,int index,int size){
        int left = 2*index + 1;
        int right = left + 1;
        int largest=0;
        while(left<size){                     //要满足只要有左节点
            if(right<size){              //如果有右节点,那么找出左右最大的,再和根节点对比
                largest = arr[right]>arr[left]?right:left; 
                largest = arr[largest]>arr[index]?largest:index;
            }
            else{largest = arr[left]>arr[index]?left:index;}//没有的话,只用左节点与根对比

            if(largest==index){break;}   

            swap(arr,index,largest);       //交换
            index = largest;               //继续下沉
            left = index*2 + 1;
        }
    }
    public static void heapInsert(int[] arr,int index){
        while(arr[index]>arr[(index-1)/2]){
            int temp = arr[index];
            arr[index] = arr[(index-1)/2];
            arr[(index-1)/2] = temp;
            index = (index-1)/2;
        }
    }

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值