算法面试题

163 阅读4分钟

字符串反转

思路:双指针

    func reverseString(_ s: inout [Character]) {
        var left = 0
        var right = s.count - 1
        while left < right {
            // 利用元组交换
            (s[right],s[left]) = (s[left],s[right])
            left += 1
            right -= 1
        }
    }

链表反转

思路:头差法

func reverseList1(_ head: ListNode?) -> ListNode? {
    var pre: ListNode?
    var head = head
    
    while head != nil {
        let next = head!.next;
        head!.next = pre
        pre = head
        head = next
    }
    return pre
}
func reverseList2(_ head: ListNode?) -> ListNode? {
    var p = head
    // 反转后的链表头部
    var newH:ListNode? = nil
    // 遍历链表
    while p != nil {
        // 记录下个节点
        let next = p!.next;
        // 当前节点next指向新链表头部
        p!.next = newH
        // 更改链表头部为当前节点
        newH = p
        // 移动p指针
        p = next
    }
    // 返回反转后的链表头节点
    return newH
}

有序数组合并

思路:两个数组分别用指针遍历对比,放到新的数组

  // 有序数组合并,注意的是合并到nums1,所以nums1是inout修饰
    static func merge(_ nums1: inout [Int], _ m: Int, _ nums2: [Int], _ n: Int) {
        let nums1copy = Array(nums1[0..<m])
        var p1 = 0 // 指向nums1copy
        var p2 = 0 // 指向nums2
        var i = 0 // 执行num1
        while p1 < m && p2 < n {
            if nums1copy[p1] < nums2[p2] {
                nums1[i] = nums1copy[p1]
                p1 += 1
            } else {
                nums1[i] = nums2[p2]
                p2 += 1
            }
            i += 1
        }
        while p1 < m {
            nums1[i] = nums1copy[p1]
            p1 += 1
            i += 1
        }
        while p2 < n {
            nums1[i] = nums2[p2]
            p2 += 1
            i += 1
        }
    }
    
    static func merge1(_ nums1: inout [Int], _ m: Int, _ nums2: [Int], _ n: Int) {
        var p1 = m - 1
        var p2 = n - 1
        var p = m + n - 1
        
        while p1 >= 0 && p2 >= 0 {
            if nums1[p1] < nums2[p2] {
                nums1[p] = nums2[p2]
                p -= 1
                p2 -= 1
            } else {
                nums1[p] = nums1[p1]
                p -= 1
                p1 -= 1
            }
        }
        // 这里要处理 num2 还有剩余的情况,因为num1保证有足够空间容纳num2, 因为 m <= n
        while p2 >= 0 {
            nums1[p] = nums2[p2]
            p2 -= 1
            p -= 1
        }
        // 或者是这种写法
//        if p2 >= 0 {
//            nums1[0...p2] = nums2[0...p2]
//        }
    }

hash算法

    func firstUniqChar(_ s: String) -> Character {
        var target: Character = " "
        // 第一次遍历,先把字符串每个字母出现次数,存到256的数组
        var arr = Array(0..<256).map { _ in return 0}
        for c in s {
            if let ascii = c.asciiValue {
                let idx = Int(ascii)
                arr[idx] += 1
            }
        }
        
        // 从头遍历存储的数组,找到第一个为1的字符
        for c in s {
            if let ascii = c.asciiValue {
                let idx = Int(ascii)
                if arr[idx] == 1 {
                    target = c
                    break
                }
            }
        }
        return target
    }

查找两个子视图的共同父视图

        //找到view1的所有父视图
        UIView *view1;
        NSMutableArray *arr1 = [NSMutableArray array];
        UIView *temp = view1;
        while (temp.superview) {
            [arr1 addObject:temp.superview];
            temp = temp.superview;
        }
        
        //找到view2的所有父视图
        UIView *view2;
        NSMutableArray *arr2 = [NSMutableArray array];
        temp = view2;
        while (temp.superview) {
            [arr2 addObject:temp.superview];
            temp = temp.superview;
        }

        //开始查找---> 倒序遍历,因为他们的最顶端的父视图肯定都是UIWindow
        int i = 0;
        NSMutableArray *resultArr = [NSMutableArray array];//他们的共同父视图可能会有好多个
        while (i < MIN(arr1.count, arr2.count)) {

            //取最后一个父视图
            UIView *super1 = arr1[arr1.count - i -1];
            UIView *super2 = arr2[arr2.count - i -1];
            
            if (super1 == super2)
            {
                [resultArr addObject:super1];
                i ++;
            }
            else//不相等说明已经没有共同的父视图了
            {
                break;
            }
        }

求无序数组当中的中位数

    /*
     解法一: 先排序后取中位数
     解法二: 利用快排思想, 效率最优
     [12,3,10,8,6,7,11,13,9]
     */
    static func findMedian(arr: inout [Int]) -> Int {
        //使用快排思想,任意挑选一个元素,以该元素为支点,划分集合为两部分。
        //如果左侧集合的长度==(n-1)/2,那么支点就是中位数。
        //如果左侧集合的长度<(n-1)/2,那么中位数在右侧,反之,中位数载左侧。
        let i = 0
        let j = arr.count - 1
        let mid = (arr.count - 1) / 2
        var div = partSort1(arr: &arr, start: i, end: j)
        while div != mid {
            if mid < div {
                div = partSort1(arr: &arr, start: i, end: div - 1)
            } else {
                div = partSort1(arr: &arr, start: div + 1, end: j)
            }
        }
        return arr[mid]
    }
    static func partSort(arr: inout [Int], start: Int, end: Int) -> Int {
        var i = start
        var j = end
        //选取关键字
        let key = arr[j]
        
        while i < j {
            //左边找比key大的值
            while i < j && arr[i] <= key {
                i += 1
            }
            //右边找比key小的值
            while i < j && arr[j] >= key {
                j -= 1
            }
            if i < j {
                //找到之后交换左右的值
                let temp = arr[i]
                arr[i] = arr[j]
                arr[j] = temp
            }
        }
        // j在中间位置,把key的值放到j的位置交换, 左边比key小,右边比key大
        let temp = arr[j]
        arr[j] = arr[end]
        arr[end] = temp
        // 这里return j 和 i 是一样的
        return j
    }

二分查找

func binarySearch<T: Comparable>(_ a: [T], key: T, range: Range<Int>) -> Int? {
    if range.lowerBound >= range.upperBound {
        // If we get here, then the search key is not present in the array.
        return nil

    } else {
        // Calculate where to split the array.
        let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2

        // Is the search key in the left half?
        if a[midIndex] > key {
            return binarySearch(a, key: key, range: range.lowerBound ..< midIndex)

        // Is the search key in the right half?
        } else if a[midIndex] < key {
            return binarySearch(a, key: key, range: midIndex + 1 ..< range.upperBound)

        // If we get here, then we've found the search key!
        } else {
            return midIndex
        }
    }
}

func binarySearch<T: Comparable>(_ a: [T], key: T) -> Int? {
    var lowerBound = 0
    var upperBound = a.count
    while lowerBound < upperBound {
        let midIndex = lowerBound + (upperBound - lowerBound) / 2
        if a[midIndex] == key {
            return midIndex
        } else if a[midIndex] < key {
            lowerBound = midIndex + 1
        } else {
            upperBound = midIndex
        }
    }
    return nil
}

总结

  • 高频: 链表反转, 查找两个子视图的共同父视图